diff --git a/.github/workflows/build-user-config.yml b/.github/workflows/build-user-config.yml index c31b6783..215ccb63 100644 --- a/.github/workflows/build-user-config.yml +++ b/.github/workflows/build-user-config.yml @@ -61,7 +61,7 @@ jobs: set -x if [ -n "${{ matrix.shield }}" ] then - EXTRA_CMAKE_ARGS="-DSHIELD=${{ matrix.shield }}" + EXTRA_CMAKE_ARGS="-DSHIELD=\"${{ matrix.shield }}\"" ARTIFACT_NAME="${{ matrix.shield }}-${{ matrix.board }}-zmk" DISPLAY_NAME="${{ matrix.shield }} - ${{ matrix.board }}" else diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a5989cef..16373673 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,11 +35,11 @@ jobs: tools/ zephyr/ bootloader/ - key: 4-${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('app/west.yml') }} + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('app/west.yml') }} restore-keys: | - 4-${{ runner.os }}-build-${{ env.cache-name }}- - 4-${{ runner.os }}-build- - 4-${{ runner.os }}- + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- timeout-minutes: 2 continue-on-error: true - name: Initialize workspace (west init) @@ -71,7 +71,7 @@ jobs: for (const shieldArgs of buildShieldArgs) { try { - const output = execSync(`west build -s app -p -b ${{ matrix.board }} -- ${shieldArgs.shield ? '-DSHIELD=' + shieldArgs.shield : ''} ${shieldArgs['cmake-args'] || ''}`); + const output = execSync(`west build -s app -p -b ${{ matrix.board }} -- ${shieldArgs.shield ? '-DSHIELD="' + shieldArgs.shield + '"' : ''} ${shieldArgs['cmake-args'] || ''}`); console.log(`::group::${{ matrix.board}} ${shieldArgs.shield} Build`) console.log(output.toString()); @@ -246,6 +246,9 @@ jobs: return hm.requires.flatMap(i => metadata.interconnects[i].boards.flatMap(b => boardAndShield(b, hm)) ); + } else { + console.warn("Unhandled shield without keys"); + return []; } break; case "interconnect": diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1f019cdc..3393d0c4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,11 +47,11 @@ jobs: tools/ zephyr/ bootloader/ - key: 4-${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('app/west.yml') }} + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('app/west.yml') }} restore-keys: | - 4-${{ runner.os }}-build-${{ env.cache-name }}- - 4-${{ runner.os }}-build- - 4-${{ runner.os }}- + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- timeout-minutes: 2 continue-on-error: true - name: Initialize workspace (west init) diff --git a/app/Kconfig b/app/Kconfig index f89d3279..9902046d 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -25,6 +25,18 @@ config USB_DEVICE_PID config USB_DEVICE_MANUFACTURER default "ZMK Project" +config BT_DIS_PNP_VID + default 0x1D50 + +config BT_DIS_PNP_PID + default 0x615E + +config BT_DIS_MODEL + default ZMK_KEYBOARD_NAME + +config BT_DIS_MANUF + default "ZMK Project" + menu "HID" choice ZMK_HID_REPORT_TYPE diff --git a/app/boards/arm/nrfmicro/Kconfig b/app/boards/arm/nrfmicro/Kconfig index 35019722..12b06621 100644 --- a/app/boards/arm/nrfmicro/Kconfig +++ b/app/boards/arm/nrfmicro/Kconfig @@ -2,9 +2,9 @@ config BOARD_ENABLE_DCDC bool "Enable DCDC mode" select SOC_DCDC_NRF52X default y - depends on (BOARD_NRFMICRO_11 || BOARD_NRFMICRO_11_FLIPPED || BOARD_NRFMICRO_13) + depends on (BOARD_NRFMICRO_11 || BOARD_NRFMICRO_11_FLIPPED || BOARD_NRFMICRO_13 || BOARD_NRFMICRO_13_52833) config BOARD_NRFMICRO_CHARGER bool "Enable battery charger" default y - depends on (BOARD_NRFMICRO_13) + depends on (BOARD_NRFMICRO_13 || BOARD_NRFMICRO_13_52833) diff --git a/app/boards/arm/nrfmicro/Kconfig.board b/app/boards/arm/nrfmicro/Kconfig.board index 36b2d55e..244242a9 100644 --- a/app/boards/arm/nrfmicro/Kconfig.board +++ b/app/boards/arm/nrfmicro/Kconfig.board @@ -14,3 +14,7 @@ config BOARD_NRFMICRO_11_FLIPPED config BOARD_NRFMICRO_13 bool "nrfmicro_13" depends on SOC_NRF52840_QIAA + +config BOARD_NRFMICRO_13_52833 + bool "nrfmicro_13_52833" + depends on SOC_NRF52833_QIAA diff --git a/app/boards/arm/nrfmicro/Kconfig.defconfig b/app/boards/arm/nrfmicro/Kconfig.defconfig index 159d9ade..751d592b 100644 --- a/app/boards/arm/nrfmicro/Kconfig.defconfig +++ b/app/boards/arm/nrfmicro/Kconfig.defconfig @@ -3,7 +3,7 @@ # Copyright (c) 2020 The ZMK Contributors # SPDX-License-Identifier: MIT -if BOARD_NRFMICRO_11 || BOARD_NRFMICRO_11_FLIPPED || BOARD_NRFMICRO_13 +if BOARD_NRFMICRO_11 || BOARD_NRFMICRO_11_FLIPPED || BOARD_NRFMICRO_13 || BOARD_NRFMICRO_13_52833 config BOARD default "nrfmicro" @@ -27,11 +27,11 @@ config ZMK_USB config PINMUX default y -if BOARD_NRFMICRO_13 +if BOARD_NRFMICRO_13 || BOARD_NRFMICRO_13_52833 config BOARD_NRFMICRO_CHARGER default y -endif # BOARD_NRFMICRO_13 +endif # BOARD_NRFMICRO_13 || BOARD_NRFMICRO_13_52833 -endif # BOARD_NRFMICRO_11 || BOARD_NRFMICRO_11_FLIPPED || BOARD_NRFMICRO_13 +endif # BOARD_NRFMICRO_11 || BOARD_NRFMICRO_11_FLIPPED || BOARD_NRFMICRO_13 || BOARD_NRFMICRO_13_52833 diff --git a/app/boards/arm/nrfmicro/arduino_pro_micro_pins_52833.dtsi b/app/boards/arm/nrfmicro/arduino_pro_micro_pins_52833.dtsi new file mode 100644 index 00000000..651edb94 --- /dev/null +++ b/app/boards/arm/nrfmicro/arduino_pro_micro_pins_52833.dtsi @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + + +/ { + pro_micro: connector { + compatible = "arduino-pro-micro"; + #gpio-cells = <2>; + gpio-map-mask = <0xffffffff 0xffffffc0>; + gpio-map-pass-thru = <0 0x3f>; + gpio-map + = <0 0 &gpio0 8 0> /* D0 */ + , <1 0 &gpio0 6 0> /* D1 */ + , <2 0 &gpio0 15 0> /* D2 */ + , <3 0 &gpio0 17 0> /* D3 */ + , <4 0 &gpio0 20 0> /* D4/A6 */ + , <5 0 &gpio0 13 0> /* D5 */ + , <6 0 &gpio0 24 0> /* D6/A7 */ + , <7 0 &gpio0 9 0> /* D7 */ + , <8 0 &gpio0 10 0> /* D8/A8 */ + , <9 0 &gpio1 6 0> /* D9/A9 */ + , <10 0 &gpio1 4 0> /* D10/A10 */ + , <16 0 &gpio0 28 0> /* D16 */ + , <14 0 &gpio0 3 0> /* D14 */ + , <15 0 &gpio1 5 0> /* D15 */ + , <18 0 &gpio0 2 0> /* D18/A0 */ + , <19 0 &gpio0 29 0> /* D19/A1 */ + , <20 0 &gpio0 31 0> /* D20/A2 */ + , <21 0 &gpio0 30 0> /* D21/A3 */ + ; + }; + + pro_micro_a: connector_a { + compatible = "arduino-pro-micro"; + #gpio-cells = <2>; + gpio-map-mask = <0xffffffff 0xffffffc0>; + gpio-map-pass-thru = <0 0x3f>; + gpio-map + = <0 0 &gpio0 2 0> /* D18/A0 */ + , <1 0 &gpio0 29 0> /* D19/A1 */ + , <2 0 &gpio0 31 0> /* D20/A2 */ + , <3 0 &gpio0 30 0> /* D21/A3 */ + , <6 0 &gpio0 20 0> /* D4/A6 */ + , <7 0 &gpio0 24 0> /* D6/A7 */ + , <8 0 &gpio0 10 0> /* D8/A8 */ + , <9 0 &gpio1 6 0> /* D9/A9 */ + , <10 0 &gpio1 11 0> /* D10/A10 */ + ; + }; +}; + + +pro_micro_d: &pro_micro {}; +pro_micro_i2c: &i2c0 {}; +pro_micro_spi: &spi0 {}; +pro_micro_serial: &uart0 {}; diff --git a/app/boards/arm/nrfmicro/nrfmicro_13_52833.dts b/app/boards/arm/nrfmicro/nrfmicro_13_52833.dts new file mode 100644 index 00000000..7f833ff5 --- /dev/null +++ b/app/boards/arm/nrfmicro/nrfmicro_13_52833.dts @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +/dts-v1/; +#include +#include "arduino_pro_micro_pins_52833.dtsi" + +/ { + model = "nrfmicro"; + compatible = "joric,nrfmicro"; + + chosen { + zephyr,code-partition = &code_partition; + zephyr,sram = &sram0; + zephyr,flash = &flash0; + zephyr,console = &cdc_acm_uart; + zmk,battery = &vbatt; + }; + + leds { + compatible = "gpio-leds"; + blue_led: led_0 { + gpios = <&gpio0 25 GPIO_ACTIVE_HIGH>; + label = "Blue LED"; + }; + }; + + ext-power { + compatible = "zmk,ext-power-generic"; + label = "EXT_POWER"; + control-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>; + }; + + vbatt: vbatt { + compatible = "zmk,battery-voltage-divider"; + label = "BATTERY"; + io-channels = <&adc 2>; + output-ohms = <2000000>; + full-ohms = <(2000000 + 820000)>; + }; +}; + +&adc { + status = "okay"; +}; + +&gpiote { + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&i2c0 { + compatible = "nordic,nrf-twi"; + sda-pin = <15>; + scl-pin = <17>; +}; + +&uart0 { + compatible = "nordic,nrf-uarte"; + tx-pin = <6>; + rx-pin = <8>; +}; + +&usbd { + status = "okay"; + cdc_acm_uart: cdc_acm_uart { + compatible = "zephyr,cdc-acm-uart"; + label = "CDC_ACM_0"; + }; +}; + + +&flash0 { + /* + * For more information, see: + * http://docs.zephyrproject.org/latest/devices/dts/flash_partitions.html + */ + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + sd_partition: partition@0 { + label = "softdevice"; + reg = <0x00000000 0x00026000>; + }; + code_partition: partition@26000 { + label = "code_partition"; + reg = <0x00026000 0x00046000>; + }; + + /* + * The flash starting at 0x0006c000 and ending at + * 0x00073fff is reserved for use by the application. + */ + + /* + * Storage partition will be used by FCB/LittleFS/NVS + * if enabled. + */ + storage_partition: partition@6c000 { + label = "storage"; + reg = <0x0006c000 0x00008000>; + }; + + boot_partition: partition@74000 { + label = "adafruit_boot"; + reg = <0x00074000 0x0000c000>; + }; + }; +}; diff --git a/app/boards/arm/nrfmicro/nrfmicro_13_52833.yaml b/app/boards/arm/nrfmicro/nrfmicro_13_52833.yaml new file mode 100644 index 00000000..768a59c9 --- /dev/null +++ b/app/boards/arm/nrfmicro/nrfmicro_13_52833.yaml @@ -0,0 +1,15 @@ +identifier: nrfmicro_13_52833 +name: nrfmicro_13_52833 +type: mcu +arch: arm +toolchain: + - zephyr + - gnuarmemb + - xtools +supported: + - adc + - usb_device + - ble + - ieee802154 + - pwm + - watchdog diff --git a/app/boards/arm/nrfmicro/nrfmicro_13_52833.zmk.yml b/app/boards/arm/nrfmicro/nrfmicro_13_52833.zmk.yml new file mode 100644 index 00000000..757ff860 --- /dev/null +++ b/app/boards/arm/nrfmicro/nrfmicro_13_52833.zmk.yml @@ -0,0 +1,10 @@ +file_format: "1" +id: nrfmicro_13_52833 +name: nRFMicro 1.3/1.4 (nRF52833) +type: board +arch: arm +outputs: + - usb + - ble +url: https://github.com/joric/nrfmicro/ +exposes: [pro_micro] diff --git a/app/boards/arm/nrfmicro/nrfmicro_13_52833_defconfig b/app/boards/arm/nrfmicro/nrfmicro_13_52833_defconfig new file mode 100644 index 00000000..112e0344 --- /dev/null +++ b/app/boards/arm/nrfmicro/nrfmicro_13_52833_defconfig @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: MIT + +CONFIG_SOC_SERIES_NRF52X=y +CONFIG_SOC_NRF52833_QIAA=y +CONFIG_BOARD_NRFMICRO_13_52833=y + +# Enable MPU +CONFIG_ARM_MPU=y + +# enable GPIO +CONFIG_GPIO=y + +CONFIG_USE_DT_CODE_PARTITION=y +CONFIG_BUILD_OUTPUT_UF2=y + +CONFIG_MPU_ALLOW_FLASH_WRITE=y +CONFIG_NVS=y +CONFIG_SETTINGS_NVS=y +CONFIG_FLASH=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_FLASH_MAP=y +CONFIG_CLOCK_CONTROL_NRF=y +CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y diff --git a/app/boards/arm/nrfmicro/pinmux.c b/app/boards/arm/nrfmicro/pinmux.c index dc3bf6c2..ef5b5521 100644 --- a/app/boards/arm/nrfmicro/pinmux.c +++ b/app/boards/arm/nrfmicro/pinmux.c @@ -14,7 +14,7 @@ static int pinmux_nrfmicro_init(const struct device *port) { ARG_UNUSED(port); -#if CONFIG_BOARD_NRFMICRO_13 +#if (CONFIG_BOARD_NRFMICRO_13 || CONFIG_BOARD_NRFMICRO_13_52833) const struct device *p0 = device_get_binding("GPIO_0"); #if CONFIG_BOARD_NRFMICRO_CHARGER gpio_pin_configure(p0, 5, GPIO_OUTPUT); diff --git a/app/boards/arm/planck/planck_rev6.keymap b/app/boards/arm/planck/planck_rev6.keymap index 47e63dc0..bbfb770e 100644 --- a/app/boards/arm/planck/planck_rev6.keymap +++ b/app/boards/arm/planck/planck_rev6.keymap @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + #include #include @@ -6,16 +12,34 @@ compatible = "zmk,keymap"; default_layer { -// ----------------------------------------------------------------------------------------- -// | TAB | Q | W | E | R | T | Y | U | I | O | P | BSPC | -// | ESC | A | S | D | F | G | H | J | K | L | ; | ' | -// | SHIFT | Z | X | C | V | B | N | M | , | . | / | RET | -// | FN | LGUI | LALT | LCTL | LOWR | SPACE | RAIS | LARW | DARW | UARW | RARW | + // ----------------------------------------------------------------------------------------- + // | TAB | Q | W | E | R | T | Y | U | I | O | P | BSPC | + // | ESC | A | S | D | F | G | H | J | K | L | ; | ' | + // | SHIFT | Z | X | C | V | B | N | M | , | . | / | RET | + // | | LCTL | LALT | LGUI | LOWR | SPACE | RAIS | LARW | DARW | UARW | RARW | bindings = < - &kp TAB &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp DEL - &kp ESC &kp A &kp S &kp D &kp F &kp G &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 N &kp M &kp COMMA &kp DOT &kp BSLH &kp RET - &trans &kp LGUI &kp LALT &kp LCTRL &trans &trans &kp SPACE &trans &kp LEFT &kp DOWN &kp UP &kp RIGHT + &kp TAB &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp BSPC + &kp ESC &kp A &kp S &kp D &kp F &kp G &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 N &kp M &kp COMMA &kp DOT &kp SLASH &kp RET + &trans &kp LCTL &kp LALT &kp LGUI &mo 1 &trans &kp SPACE &mo 2 &kp LEFT &kp DOWN &kp UP &kp RIGHT + >; + }; + + lower { + bindings = < + &kp LS(GRAVE) &kp LS(N1) &kp LS(N2) &kp LS(N3) &kp LS(N4) &kp LS(N5) &kp LS(N6) &kp LS(N7) &kp LS(N8) &kp LS(N9) &kp LS(N0) &kp DEL + &kp DEL &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp UNDER &kp PLUS &kp LBRC &kp RBRC &kp PIPE + &trans &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 &kp LS(HASH) &kp LS(BSLH) &kp HOME &kp END &trans + &trans &trans &trans &trans &trans &trans &trans &trans &kp C_NEXT &kp C_VOL_DN &kp C_VOL_UP &kp C_PP + >; + }; + + raise { + bindings = < + &kp GRAVE &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp BSPC + &kp DEL &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp MINUS &kp EQUAL &kp LBKT &kp RBKT &kp BSLH + &trans &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 &kp HASH &kp BSLH &kp PG_UP &kp PG_DN &trans + &reset &bootloader &trans &trans &trans &trans &trans &trans &kp C_NEXT &kp C_VOL_DN &kp C_VOL_UP &kp C_PP >; }; }; diff --git a/app/boards/arm/puchi_ble/CMakeLists.txt b/app/boards/arm/puchi_ble/CMakeLists.txt new file mode 100644 index 00000000..12cf9b1c --- /dev/null +++ b/app/boards/arm/puchi_ble/CMakeLists.txt @@ -0,0 +1,6 @@ + +if(CONFIG_PINMUX) +zephyr_library() +zephyr_library_sources(pinmux.c) +zephyr_library_include_directories(${ZEPHYR_BASE}/drivers) +endif() \ No newline at end of file diff --git a/app/boards/arm/puchi_ble/Kconfig b/app/boards/arm/puchi_ble/Kconfig new file mode 100644 index 00000000..719c3845 --- /dev/null +++ b/app/boards/arm/puchi_ble/Kconfig @@ -0,0 +1,3 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + diff --git a/app/boards/arm/puchi_ble/Kconfig.board b/app/boards/arm/puchi_ble/Kconfig.board new file mode 100644 index 00000000..07638885 --- /dev/null +++ b/app/boards/arm/puchi_ble/Kconfig.board @@ -0,0 +1,8 @@ +# Puchi-BLE board configuration + +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config BOARD_PUCHI_BLE_v1 + bool "puchi_ble_v1" + depends on SOC_NRF52840_QIAA diff --git a/app/boards/arm/puchi_ble/Kconfig.defconfig b/app/boards/arm/puchi_ble/Kconfig.defconfig new file mode 100644 index 00000000..94d12c17 --- /dev/null +++ b/app/boards/arm/puchi_ble/Kconfig.defconfig @@ -0,0 +1,28 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +if BOARD_PUCHI_BLE_v1 + +config BOARD + default "puchi_ble" + +if USB_DEVICE_STACK + +config USB_NRFX + default y + +endif # USB_DEVICE_STACK + +config BT_CTLR + default BT + +config ZMK_BLE + default y + +config ZMK_USB + default y + +config PINMUX + default y + +endif # BOARD_PUCHI_BLE_v1 diff --git a/app/boards/arm/puchi_ble/arduino_pro_micro_pins.dtsi b/app/boards/arm/puchi_ble/arduino_pro_micro_pins.dtsi new file mode 100644 index 00000000..ed3317f1 --- /dev/null +++ b/app/boards/arm/puchi_ble/arduino_pro_micro_pins.dtsi @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + + +/ { + pro_micro: connector { + compatible = "arduino-pro-micro"; + #gpio-cells = <2>; + gpio-map-mask = <0xffffffff 0xffffffc0>; + gpio-map-pass-thru = <0 0x3f>; + gpio-map + = <0 0 &gpio0 8 0> /* D0 */ + , <1 0 &gpio0 6 0> /* D1 */ + , <2 0 &gpio0 15 0> /* D2 */ + , <3 0 &gpio0 17 0> /* D3 */ + , <4 0 &gpio0 20 0> /* D4/A6 */ + , <5 0 &gpio0 13 0> /* D5 */ + , <6 0 &gpio0 24 0> /* D6/A7 */ + , <7 0 &gpio0 9 0> /* D7 */ + , <8 0 &gpio0 10 0> /* D8/A8 */ + , <9 0 &gpio1 6 0> /* D9/A9 */ + , <10 0 &gpio1 11 0> /* D10/A10 */ + , <16 0 &gpio0 28 0> /* D16 */ + , <14 0 &gpio0 3 0> /* D14 */ + , <15 0 &gpio1 13 0> /* D15 */ + , <18 0 &gpio0 2 0> /* D18/A0 */ + , <19 0 &gpio0 29 0> /* D19/A1 */ + , <20 0 &gpio0 31 0> /* D20/A2 */ + , <21 0 &gpio0 30 0> /* D21/A3 */ + ; + }; + + pro_micro_a: connector_a { + compatible = "arduino-pro-micro"; + #gpio-cells = <2>; + gpio-map-mask = <0xffffffff 0xffffffc0>; + gpio-map-pass-thru = <0 0x3f>; + gpio-map + = <0 0 &gpio0 2 0> /* D18/A0 */ + , <1 0 &gpio0 29 0> /* D19/A1 */ + , <2 0 &gpio0 31 0> /* D20/A2 */ + , <3 0 &gpio0 30 0> /* D21/A3 */ + , <6 0 &gpio0 20 0> /* D4/A6 */ + , <7 0 &gpio0 24 0> /* D6/A7 */ + , <8 0 &gpio0 10 0> /* D8/A8 */ + , <9 0 &gpio1 6 0> /* D9/A9 */ + , <10 0 &gpio1 11 0> /* D10/A10 */ + ; + }; +}; + + +pro_micro_d: &pro_micro {}; +pro_micro_i2c: &i2c0 {}; +pro_micro_spi: &spi0 {}; +pro_micro_serial: &uart0 {}; diff --git a/app/boards/arm/puchi_ble/board.cmake b/app/boards/arm/puchi_ble/board.cmake new file mode 100644 index 00000000..3b5c4aea --- /dev/null +++ b/app/boards/arm/puchi_ble/board.cmake @@ -0,0 +1,6 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +board_runner_args(nrfjprog "--nrf-family=NRF52" "--softreset") +include(${ZEPHYR_BASE}/boards/common/blackmagicprobe.board.cmake) +include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake) diff --git a/app/boards/arm/puchi_ble/pinmux.c b/app/boards/arm/puchi_ble/pinmux.c new file mode 100644 index 00000000..78cea314 --- /dev/null +++ b/app/boards/arm/puchi_ble/pinmux.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#include +#include + +static int pinmux_puchi_ble_init(const struct device *port) { + ARG_UNUSED(port); + +#if CONFIG_BOARD_PUCHI_BLE_v1 + const struct device *p0 = device_get_binding("GPIO_0"); +#if CONFIG_BOARD_PUCHI_BLE_CHARGER + gpio_pin_configure(p0, 5, GPIO_OUTPUT); + gpio_pin_set(p0, 5, 0); +#else + gpio_pin_configure(p0, 5, GPIO_INPUT); +#endif +#endif + return 0; +} + +SYS_INIT(pinmux_puchi_ble_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); diff --git a/app/boards/arm/puchi_ble/puchi_ble_v1.dts b/app/boards/arm/puchi_ble/puchi_ble_v1.dts new file mode 100644 index 00000000..e324cccc --- /dev/null +++ b/app/boards/arm/puchi_ble/puchi_ble_v1.dts @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +/dts-v1/; +#include +#include "arduino_pro_micro_pins.dtsi" + +/ { + model = "puchi_ble"; + compatible = "puchi_ble"; + + chosen { + zephyr,code-partition = &code_partition; + zephyr,sram = &sram0; + zephyr,flash = &flash0; + zephyr,console = &cdc_acm_uart; + zmk,battery = &vbatt; + }; + + leds { + compatible = "gpio-leds"; + blue_led: led_0 { + gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>; + label = "Blue LED"; + }; + }; + + ext-power { + compatible = "zmk,ext-power-generic"; + label = "EXT_POWER"; + control-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>; + }; + + vbatt: vbatt { + compatible = "zmk,battery-voltage-divider"; + label = "BATTERY"; + io-channels = <&adc 2>; + output-ohms = <2000000>; + full-ohms = <(2000000 + 820000)>; + }; +}; + +&adc { + status = "okay"; +}; + +&gpiote { + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&i2c0 { + compatible = "nordic,nrf-twi"; + sda-pin = <15>; + scl-pin = <17>; +}; + +&uart0 { + compatible = "nordic,nrf-uarte"; + tx-pin = <6>; + rx-pin = <8>; +}; + +&usbd { + status = "okay"; + cdc_acm_uart: cdc_acm_uart { + compatible = "zephyr,cdc-acm-uart"; + label = "CDC_ACM_0"; + }; +}; + + +&flash0 { + /* + * For more information, see: + * http://docs.zephyrproject.org/latest/devices/dts/flash_partitions.html + */ + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + sd_partition: partition@0 { + label = "softdevice"; + reg = <0x00000000 0x00026000>; + }; + code_partition: partition@26000 { + label = "code_partition"; + reg = <0x00026000 0x000c6000>; + }; + + /* + * The flash starting at 0x000ec000 and ending at + * 0x000f3fff is reserved for use by the application. + */ + + /* + * Storage partition will be used by FCB/LittleFS/NVS + * if enabled. + */ + storage_partition: partition@ec000 { + label = "storage"; + reg = <0x000ec000 0x00008000>; + }; + + boot_partition: partition@f4000 { + label = "adafruit_boot"; + reg = <0x000f4000 0x0000c000>; + }; + }; +}; diff --git a/app/boards/arm/puchi_ble/puchi_ble_v1.yaml b/app/boards/arm/puchi_ble/puchi_ble_v1.yaml new file mode 100644 index 00000000..18770722 --- /dev/null +++ b/app/boards/arm/puchi_ble/puchi_ble_v1.yaml @@ -0,0 +1,15 @@ +identifier: puchi_ble_v1 +name: puchi_ble_v1 +type: mcu +arch: arm +toolchain: + - zephyr + - gnuarmemb + - xtools +supported: + - adc + - usb_device + - ble + - ieee802154 + - pwm + - watchdog diff --git a/app/boards/arm/puchi_ble/puchi_ble_v1.zmk.yml b/app/boards/arm/puchi_ble/puchi_ble_v1.zmk.yml new file mode 100644 index 00000000..f3114008 --- /dev/null +++ b/app/boards/arm/puchi_ble/puchi_ble_v1.zmk.yml @@ -0,0 +1,10 @@ +file_format: "1" +id: puchi_ble_v1 +name: Puchi-BLE V1 +type: board +arch: arm +outputs: + - usb + - ble +url: https://keycapsss.com/keyboard-parts/mcu-controller/202/puchi-ble-wireless-microcontroller +exposes: [pro_micro] diff --git a/app/boards/arm/puchi_ble/puchi_ble_v1_defconfig b/app/boards/arm/puchi_ble/puchi_ble_v1_defconfig new file mode 100644 index 00000000..e32886d0 --- /dev/null +++ b/app/boards/arm/puchi_ble/puchi_ble_v1_defconfig @@ -0,0 +1,24 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +CONFIG_SOC_SERIES_NRF52X=y +CONFIG_SOC_NRF52840_QIAA=y +CONFIG_BOARD_PUCHI_BLE_v1=y + +# Enable MPU +CONFIG_ARM_MPU=y + +# enable GPIO +CONFIG_GPIO=y + +CONFIG_USE_DT_CODE_PARTITION=y +CONFIG_BUILD_OUTPUT_UF2=y + +CONFIG_MPU_ALLOW_FLASH_WRITE=y +CONFIG_NVS=y +CONFIG_SETTINGS_NVS=y +CONFIG_FLASH=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_FLASH_MAP=y +CONFIG_CLOCK_CONTROL_NRF=y +CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y diff --git a/app/boards/shields/crbn/README.md b/app/boards/shields/crbn/README.md deleted file mode 100644 index fee388f1..00000000 --- a/app/boards/shields/crbn/README.md +++ /dev/null @@ -1 +0,0 @@ -A 40% Ortho keyboard made by Polarity Works diff --git a/app/boards/shields/crbn/crbn.keymap b/app/boards/shields/crbn/crbn.keymap index d9aeaa95..9423e1af 100644 --- a/app/boards/shields/crbn/crbn.keymap +++ b/app/boards/shields/crbn/crbn.keymap @@ -13,16 +13,16 @@ compatible = "zmk,keymap"; default_layer { - // ----------------------------------------------------------------------------------------- - // | TAB | Q | W | E | R | T | Y | U | I | O | P | BSPC | - // | ESC | A | S | D | F | G | H | J | K | L | ; | ' | - // | SHIFT | Z | X | C | V | B | N | M | , | . | / | RET | - // | FN | LGUI | LALT | LCTL | LOWR | SPACE | RAIS | LARW | DARW | UARW | RARW | + // ----------------------------------------------------------------------------------------- + // | TAB | Q | W | E | R | T | Y | U | I | O | P | BSPC | + // | ESC | A | S | D | F | G | H | J | K | L | ; | ' | + // | SHIFT | Z | X | C | V | B | N | M | , | . | / | RET | + // | | LCTL | LALT | LGUI | LOWR | SPACE | RAIS | LARW | DARW | UARW | RARW | bindings = < &kp TAB &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp BSPC &kp ESC &kp A &kp S &kp D &kp F &kp G &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 N &kp M &kp COMMA &kp DOT &kp BSLH &kp RET - &trans &kp LGUI &kp LALT &kp LCTRL &mo 1 &kp SPACE &trans &mo 2 &kp LEFT &kp DOWN &kp UP &kp RIGHT + &kp LSHFT &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp SLASH &kp RET + &trans &kp LCTL &kp LALT &kp LGUI &mo 1 &kp SPACE &trans &mo 2 &kp LEFT &kp DOWN &kp UP &kp RIGHT >; sensor-bindings = <&inc_dec_kp PG_UP PG_DN>; @@ -31,7 +31,7 @@ lower { bindings = < &kp LS(GRAVE) &kp LS(N1) &kp LS(N2) &kp LS(N3) &kp LS(N4) &kp LS(N5) &kp LS(N6) &kp LS(N7) &kp LS(N8) &kp LS(N9) &kp LS(N0) &kp DEL - &kp DEL &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp UNDER &kp PLUS &kp LT &kp GT &kp PIPE + &kp DEL &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp UNDER &kp PLUS &kp LBRC &kp RBRC &kp PIPE &trans &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 &kp LS(HASH) &kp LS(BSLH) &kp HOME &kp END &trans &trans &trans &trans &trans &trans &trans &trans &mo 3 &kp C_NEXT &kp C_VOL_DN &kp C_VOL_UP &kp C_PP >; @@ -46,12 +46,14 @@ &trans &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 &kp HASH &kp BSLH &kp PG_UP &kp PG_DN &trans &trans &trans &trans &trans &mo 3 &trans &trans &trans &kp C_NEXT &kp C_VOL_DN &kp C_VOL_UP &kp C_PP >; + + sensor-bindings = <&inc_dec_kp PG_UP PG_DN>; }; - + control { bindings = < &reset &bootloader &bt BT_CLR &bt BT_PRV &bt BT_NXT &trans &trans &trans &trans &trans &trans &trans - &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans + &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &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 &trans &trans &trans &trans &trans >; diff --git a/app/boards/shields/eternal_keypad/Kconfig.defconfig b/app/boards/shields/eternal_keypad/Kconfig.defconfig new file mode 100644 index 00000000..4d4195ef --- /dev/null +++ b/app/boards/shields/eternal_keypad/Kconfig.defconfig @@ -0,0 +1,9 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +if SHIELD_ETERNAL_KEYPAD || SHIELD_ETERNAL_KEYPAD_LEFTY + +config ZMK_KEYBOARD_NAME + default "Eternal Keypad" + +endif diff --git a/app/boards/shields/eternal_keypad/Kconfig.shield b/app/boards/shields/eternal_keypad/Kconfig.shield new file mode 100644 index 00000000..4a59379e --- /dev/null +++ b/app/boards/shields/eternal_keypad/Kconfig.shield @@ -0,0 +1,8 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config SHIELD_ETERNAL_KEYPAD + def_bool $(shields_list_contains,eternal_keypad) + +config SHIELD_ETERNAL_KEYPAD_LEFTY + def_bool $(shields_list_contains,eternal_keypad_lefty) diff --git a/app/boards/shields/eternal_keypad/README.md b/app/boards/shields/eternal_keypad/README.md new file mode 100644 index 00000000..4b217c5f --- /dev/null +++ b/app/boards/shields/eternal_keypad/README.md @@ -0,0 +1,10 @@ +# Eternal Keypad + +Eternal Keypad is an open-source input device for gaming that can be assembled for both right and left hand mouse users. + +Firmware is described in the [Eternal Keypad](https://github.com/duckyb/eternal-keypad) repository [README](https://github.com/duckyb/eternal-keypad#firmware). + +Two keymaps are included: + +- eternal_keypad (default) +- eternal_keypad_lefty (for left handed users) diff --git a/app/boards/shields/eternal_keypad/boards/nice_nano_v2.overlay b/app/boards/shields/eternal_keypad/boards/nice_nano_v2.overlay new file mode 100644 index 00000000..61950d18 --- /dev/null +++ b/app/boards/shields/eternal_keypad/boards/nice_nano_v2.overlay @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +&spi1 { + compatible = "nordic,nrf-spim"; + /* Cannot be used together with i2c0. */ + status = "okay"; + mosi-pin = <6>; + // Unused pins, needed for SPI definition, but not used by the ws2812 driver itself. + sck-pin = <5>; + miso-pin = <7>; + + led_strip: ws2812@0 { + compatible = "worldsemi,ws2812-spi"; + label = "SK6812mini"; + + /* SPI */ + reg = <0>; /* ignored, but necessary for SPI bindings */ + spi-max-frequency = <4000000>; + + /* WS2812 */ + chain-length = <8>; + spi-one-frame = <0x70>; + spi-zero-frame = <0x40>; + color-mapping = ; + }; +}; + +/ { + chosen { + zmk,underglow = &led_strip; + }; +}; diff --git a/app/boards/shields/eternal_keypad/eternal_keypad.conf b/app/boards/shields/eternal_keypad/eternal_keypad.conf new file mode 100644 index 00000000..65fa2955 --- /dev/null +++ b/app/boards/shields/eternal_keypad/eternal_keypad.conf @@ -0,0 +1,9 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +# Uncomment to turn on logging, and set ZMK logging to debug output +# CONFIG_ZMK_USB_LOGGING=y + +# Uncomment the following lines to enable RGB underglow +# CONFIG_ZMK_RGB_UNDERGLOW=y +# CONFIG_WS2812_STRIP=y diff --git a/app/boards/shields/eternal_keypad/eternal_keypad.dtsi b/app/boards/shields/eternal_keypad/eternal_keypad.dtsi new file mode 100644 index 00000000..6319d9e0 --- /dev/null +++ b/app/boards/shields/eternal_keypad/eternal_keypad.dtsi @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +/ { + chosen { + zmk,kscan = &kscan0; + zmk,matrix_transform = &default_transform; + }; + + kscan0: kscan { + compatible = "zmk,kscan-gpio-matrix"; + label = "KSCAN"; + + diode-direction = "col2row"; + + row-gpios + = <&pro_micro 9 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + ; + + col-gpios + = <&pro_micro 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 16 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 14 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 15 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 18 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 19 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 20 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + , <&pro_micro 21 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> + ; + }; + + default_transform: keymap_transform_0 { + compatible = "zmk,matrix-transform"; + columns = <8>; + rows = <5>; + map = < + RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) + RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) 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,7) + 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(4,0) RC(4,1) RC(4,2) RC(4,3) RC(4,5) RC(4,7) + >; + }; +}; diff --git a/app/boards/shields/eternal_keypad/eternal_keypad.keymap b/app/boards/shields/eternal_keypad/eternal_keypad.keymap new file mode 100644 index 00000000..0f3eda7b --- /dev/null +++ b/app/boards/shields/eternal_keypad/eternal_keypad.keymap @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#include + +#define BASE 0 +#define ARROW 1 +#define FUNC 2 + +/ { + chosen { + zmk,kscan = &kscan0; + zmk,matrix_transform = &default_transform; + }; + + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &kp ESC &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 + &kp TAB &kp Q &kp W &kp E &kp R &kp T &kp N7 + &kp F13 &kp ENTER &kp A &kp S &kp D &kp F &kp G &kp N8 + &kp F14 &kp LSHFT &kp Z &kp X &kp C &kp V &kp B &kp N9 + &kp F15 &kp LCTRL &sl FUNC &kp LALT &kp SPACE < ARROW N0 + >; + }; + + arrow_layer { + bindings = < + &bt BT_SEL 0 &bt BT_SEL 1 &trans &trans &trans &out OUT_USB &out OUT_BLE + &trans &trans &kp UP &trans &rgb_ug RGB_TOG &rgb_ug RGB_HUI &rgb_ug RGB_HUD + &bt BT_CLR &trans &kp LEFT &kp DOWN &kp RIGHT &trans &rgb_ug RGB_BRI &rgb_ug RGB_BRD + &reset &trans &trans &trans &trans &trans &rgb_ug RGB_EFF &rgb_ug RGB_EFR + &bootloader &trans &trans &trans &trans &trans + >; + }; + + function_layer { + bindings = < + &trans &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 + &trans &kp P &kp O &kp I &kp U &kp Y &kp F7 + &bt BT_CLR &kp BSPC &kp SEMI &kp L &kp K &kp J &kp H &kp F8 + &reset &trans &kp LGUI &kp M &kp N &kp F12 &kp F11 &kp F9 + &bootloader &trans &trans &trans &trans &kp F10 + >; + }; + }; +}; diff --git a/app/boards/shields/eternal_keypad/eternal_keypad.overlay b/app/boards/shields/eternal_keypad/eternal_keypad.overlay new file mode 100644 index 00000000..4828baf7 --- /dev/null +++ b/app/boards/shields/eternal_keypad/eternal_keypad.overlay @@ -0,0 +1,7 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include "eternal_keypad.dtsi" diff --git a/app/boards/shields/eternal_keypad/eternal_keypad.zmk.yml b/app/boards/shields/eternal_keypad/eternal_keypad.zmk.yml new file mode 100644 index 00000000..c5c86bc7 --- /dev/null +++ b/app/boards/shields/eternal_keypad/eternal_keypad.zmk.yml @@ -0,0 +1,9 @@ +file_format: "1" +id: eternal_keypad +name: Eternal Keypad +type: shield +url: https://github.com/duckyb/eternal-keypad +requires: [pro_micro] +features: + - keys + - underglow diff --git a/app/boards/shields/eternal_keypad/eternal_keypad_lefty.keymap b/app/boards/shields/eternal_keypad/eternal_keypad_lefty.keymap new file mode 100644 index 00000000..e1bddbad --- /dev/null +++ b/app/boards/shields/eternal_keypad/eternal_keypad_lefty.keymap @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#include + +#define BASE 0 +#define ARROW 1 +#define FUNC 2 + +/ { + chosen { + zmk,kscan = &kscan0; + zmk,matrix_transform = &default_transform; + }; + + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &kp ESC &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 + &kp TAB &kp Q &kp W &kp E &kp R &kp T &kp N7 + &kp F13 &kp ENTER &kp D &kp S &kp A &kp F &kp G &kp N8 + &kp F14 &kp LSHFT &kp Z &kp X &kp C &kp V &kp B &kp N9 + &kp F15 &kp LCTRL &sl FUNC &kp LALT &kp SPACE < ARROW N0 + >; + }; + + arrow_layer { + bindings = < + &bt BT_SEL 0 &bt BT_SEL 1 &trans &trans &trans &out OUT_USB &out OUT_BLE + &trans &trans &kp UP &trans &rgb_ug RGB_TOG &rgb_ug RGB_HUI &rgb_ug RGB_HUD + &bt BT_CLR &trans &kp RIGHT &kp DOWN &kp RIGHT &trans &rgb_ug RGB_BRI &rgb_ug RGB_BRD + &reset &trans &trans &trans &trans &trans &rgb_ug RGB_EFF &rgb_ug RGB_EFR + &bootloader &trans &trans &trans &trans &trans + >; + }; + + function_layer { + bindings = < + &trans &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 + &trans &kp P &kp O &kp I &kp U &kp Y &kp F7 + &bt BT_CLR &kp BSPC &kp SEMI &kp L &kp K &kp J &kp H &kp F8 + &reset &trans &kp LGUI &kp M &kp N &kp F12 &kp F11 &kp F9 + &bootloader &trans &trans &trans &trans &kp F10 + >; + }; + }; +}; diff --git a/app/boards/shields/eternal_keypad/eternal_keypad_lefty.overlay b/app/boards/shields/eternal_keypad/eternal_keypad_lefty.overlay new file mode 100644 index 00000000..0c5599a1 --- /dev/null +++ b/app/boards/shields/eternal_keypad/eternal_keypad_lefty.overlay @@ -0,0 +1,7 @@ +/* + * Copyright (c) 202 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include "eternal_keypad.dtsi" diff --git a/app/boards/shields/eternal_keypad/eternal_keypad_lefty.zmk.yml b/app/boards/shields/eternal_keypad/eternal_keypad_lefty.zmk.yml new file mode 100644 index 00000000..9f5fb578 --- /dev/null +++ b/app/boards/shields/eternal_keypad/eternal_keypad_lefty.zmk.yml @@ -0,0 +1,9 @@ +file_format: "1" +id: eternal_keypad_lefty +name: Eternal Keypad Lefty +type: shield +url: https://github.com/duckyb/eternal-keypad +requires: [pro_micro] +features: + - keys + - underglow diff --git a/app/boards/shields/nice_view/Kconfig.defconfig b/app/boards/shields/nice_view/Kconfig.defconfig new file mode 100644 index 00000000..622c7498 --- /dev/null +++ b/app/boards/shields/nice_view/Kconfig.defconfig @@ -0,0 +1,22 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +if SHIELD_NICE_VIEW + +config ZMK_DISPLAY + select LVGL_FONT_MONTSERRAT_26 + +if ZMK_DISPLAY + +config SPI + default y + +config LS0XX + default y + +config ZMK_WIDGET_WPM_STATUS + default y if !ZMK_SPLIT || ZMK_SPLIT_ROLE_CENTRAL + +endif # ZMK_DISPLAY + +endif diff --git a/app/boards/shields/nice_view/Kconfig.shield b/app/boards/shields/nice_view/Kconfig.shield new file mode 100644 index 00000000..55cba788 --- /dev/null +++ b/app/boards/shields/nice_view/Kconfig.shield @@ -0,0 +1,5 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config SHIELD_NICE_VIEW + def_bool $(shields_list_contains,nice_view) diff --git a/app/boards/shields/nice_view/README.md b/app/boards/shields/nice_view/README.md new file mode 100644 index 00000000..0b4ac21f --- /dev/null +++ b/app/boards/shields/nice_view/README.md @@ -0,0 +1,5 @@ +# nice!view + +The nice!view is a low power, high refresh rate display meant to replace I2C OLEDs traditionally used. + +This shield requires that an `&nice_view_spi` labelled SPI bus is provided with *at least* MOSI, SCK, and CS pins defined. diff --git a/app/boards/shields/nice_view/nice_view.conf b/app/boards/shields/nice_view/nice_view.conf new file mode 100644 index 00000000..2c1d6c71 --- /dev/null +++ b/app/boards/shields/nice_view/nice_view.conf @@ -0,0 +1,5 @@ +# Enable nice!view +CONFIG_ZMK_DISPLAY=y +CONFIG_LVGL_THEME_DEFAULT_FONT_SMALL_MONTSERRAT_26=y +CONFIG_LVGL_THEME_DEFAULT_FONT_NORMAL_MONTSERRAT_26=y +CONFIG_ZMK_DISPLAY_BLANK_ON_IDLE=n diff --git a/app/boards/shields/nice_view/nice_view.overlay b/app/boards/shields/nice_view/nice_view.overlay new file mode 100644 index 00000000..eacdd41a --- /dev/null +++ b/app/boards/shields/nice_view/nice_view.overlay @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +&nice_view_spi { + status = "okay"; + nice_view: ls0xx@0 { + compatible = "sharp,ls0xx"; + label = "DISPLAY"; + spi-max-frequency = <1000000>; + reg = <0>; + width = <160>; + height = <68>; + }; +}; + +/ { + chosen { + zephyr,display = &nice_view; + }; +}; diff --git a/app/boards/shields/nice_view/nice_view.zmk.yml b/app/boards/shields/nice_view/nice_view.zmk.yml new file mode 100644 index 00000000..04b98a8a --- /dev/null +++ b/app/boards/shields/nice_view/nice_view.zmk.yml @@ -0,0 +1,8 @@ +file_format: "1" +id: nice_view +name: nice!view +type: shield +url: https://nicekeyboards.com/nice-view +requires: [nice_view_header] +features: + - display diff --git a/app/boards/shields/nice_view_adapter/Kconfig.defconfig b/app/boards/shields/nice_view_adapter/Kconfig.defconfig new file mode 100644 index 00000000..fb23f20c --- /dev/null +++ b/app/boards/shields/nice_view_adapter/Kconfig.defconfig @@ -0,0 +1,2 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT diff --git a/app/boards/shields/nice_view_adapter/Kconfig.shield b/app/boards/shields/nice_view_adapter/Kconfig.shield new file mode 100644 index 00000000..bf9ba7cb --- /dev/null +++ b/app/boards/shields/nice_view_adapter/Kconfig.shield @@ -0,0 +1,5 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config SHIELD_NICE_VIEW_ADAPTER + def_bool $(shields_list_contains,nice_view_adapter) diff --git a/app/boards/shields/nice_view_adapter/README.md b/app/boards/shields/nice_view_adapter/README.md new file mode 100644 index 00000000..ec4665a3 --- /dev/null +++ b/app/boards/shields/nice_view_adapter/README.md @@ -0,0 +1,11 @@ +# nice!view Adapter + +This shield is used as an adapter between the nice!view and existing shields/boards that expose an I2C OLED header. + +To use this shield, you should add this shield to your list of shields *before* `nice_view`. + +The nice!view will use the SDA/SCL pins of the OLED, and then the adapter expects a final pin to be "bodged" from your microcontroller to the nice!view CS pin. This adapter assumes that the CS pin bodged is the `&pro_micro 1` pin or "D1", which is the top left pin when looking at the front of the board. If you can't use this pin, you'll need to override the `cs-gpios` for the `&nice_view_spi` bus (in your `zmk-config` keymap for example) or you will want to define your own `&nice_view_spi` bus without using this adapter. + +``` +west build -b nice_nano_v2 -- -DSHIELD="lily58_left nice_view_adapter nice_view" +``` diff --git a/app/boards/shields/nice_view_adapter/boards/bluemicro840_v1.overlay b/app/boards/shields/nice_view_adapter/boards/bluemicro840_v1.overlay new file mode 100644 index 00000000..9d9ab734 --- /dev/null +++ b/app/boards/shields/nice_view_adapter/boards/bluemicro840_v1.overlay @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +nice_view_spi: &spi0 { + compatible = "nordic,nrf-spim"; + sck-pin = <17>; + mosi-pin = <15>; + miso-pin = <25>; + cs-gpios = <&pro_micro 1 GPIO_ACTIVE_HIGH>; +}; + +&pro_micro_i2c { + status = "disabled"; +}; diff --git a/app/boards/shields/nice_view_adapter/boards/mikoto_520.overlay b/app/boards/shields/nice_view_adapter/boards/mikoto_520.overlay new file mode 100644 index 00000000..9f6fad1c --- /dev/null +++ b/app/boards/shields/nice_view_adapter/boards/mikoto_520.overlay @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +nice_view_spi: &spi0 { + compatible = "nordic,nrf-spim"; + sck-pin = <20>; + mosi-pin = <17>; + miso-pin = <5>; + cs-gpios = <&pro_micro 1 GPIO_ACTIVE_HIGH>; +}; + +&pro_micro_i2c { + status = "disabled"; +}; diff --git a/app/boards/shields/nice_view_adapter/boards/nice_nano.overlay b/app/boards/shields/nice_view_adapter/boards/nice_nano.overlay new file mode 100644 index 00000000..3a0ad462 --- /dev/null +++ b/app/boards/shields/nice_view_adapter/boards/nice_nano.overlay @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +nice_view_spi: &spi0 { + compatible = "nordic,nrf-spim"; + sck-pin = <20>; + mosi-pin = <17>; + miso-pin = <25>; + cs-gpios = <&pro_micro 1 GPIO_ACTIVE_HIGH>; +}; + +&pro_micro_i2c { + status = "disabled"; +}; diff --git a/app/boards/shields/nice_view_adapter/boards/nice_nano_v2.overlay b/app/boards/shields/nice_view_adapter/boards/nice_nano_v2.overlay new file mode 100644 index 00000000..3a0ad462 --- /dev/null +++ b/app/boards/shields/nice_view_adapter/boards/nice_nano_v2.overlay @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +nice_view_spi: &spi0 { + compatible = "nordic,nrf-spim"; + sck-pin = <20>; + mosi-pin = <17>; + miso-pin = <25>; + cs-gpios = <&pro_micro 1 GPIO_ACTIVE_HIGH>; +}; + +&pro_micro_i2c { + status = "disabled"; +}; diff --git a/app/boards/shields/nice_view_adapter/boards/nrfmicro_11.overlay b/app/boards/shields/nice_view_adapter/boards/nrfmicro_11.overlay new file mode 100644 index 00000000..9d9ab734 --- /dev/null +++ b/app/boards/shields/nice_view_adapter/boards/nrfmicro_11.overlay @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +nice_view_spi: &spi0 { + compatible = "nordic,nrf-spim"; + sck-pin = <17>; + mosi-pin = <15>; + miso-pin = <25>; + cs-gpios = <&pro_micro 1 GPIO_ACTIVE_HIGH>; +}; + +&pro_micro_i2c { + status = "disabled"; +}; diff --git a/app/boards/shields/nice_view_adapter/boards/nrfmicro_11_flipped.overlay b/app/boards/shields/nice_view_adapter/boards/nrfmicro_11_flipped.overlay new file mode 100644 index 00000000..7b12c252 --- /dev/null +++ b/app/boards/shields/nice_view_adapter/boards/nrfmicro_11_flipped.overlay @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +nice_view_spi: &spi0 { + compatible = "nordic,nrf-spim"; + sck-pin = <31>; + mosi-pin = <30>; + miso-pin = <25>; + cs-gpios = <&pro_micro 1 GPIO_ACTIVE_HIGH>; +}; + +&pro_micro_i2c { + status = "disabled"; +}; diff --git a/app/boards/shields/nice_view_adapter/boards/nrfmicro_13.overlay b/app/boards/shields/nice_view_adapter/boards/nrfmicro_13.overlay new file mode 100644 index 00000000..9d9ab734 --- /dev/null +++ b/app/boards/shields/nice_view_adapter/boards/nrfmicro_13.overlay @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +nice_view_spi: &spi0 { + compatible = "nordic,nrf-spim"; + sck-pin = <17>; + mosi-pin = <15>; + miso-pin = <25>; + cs-gpios = <&pro_micro 1 GPIO_ACTIVE_HIGH>; +}; + +&pro_micro_i2c { + status = "disabled"; +}; diff --git a/app/boards/shields/nice_view_adapter/boards/puchi_ble_v1.overlay b/app/boards/shields/nice_view_adapter/boards/puchi_ble_v1.overlay new file mode 100644 index 00000000..9d9ab734 --- /dev/null +++ b/app/boards/shields/nice_view_adapter/boards/puchi_ble_v1.overlay @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +nice_view_spi: &spi0 { + compatible = "nordic,nrf-spim"; + sck-pin = <17>; + mosi-pin = <15>; + miso-pin = <25>; + cs-gpios = <&pro_micro 1 GPIO_ACTIVE_HIGH>; +}; + +&pro_micro_i2c { + status = "disabled"; +}; diff --git a/app/boards/shields/nice_view_adapter/nice_view_adapter.conf b/app/boards/shields/nice_view_adapter/nice_view_adapter.conf new file mode 100644 index 00000000..c5fe224e --- /dev/null +++ b/app/boards/shields/nice_view_adapter/nice_view_adapter.conf @@ -0,0 +1,2 @@ +# Disable OLED +CONFIG_SSD1306=n diff --git a/app/boards/shields/nice_view_adapter/nice_view_adapter.overlay b/app/boards/shields/nice_view_adapter/nice_view_adapter.overlay new file mode 100644 index 00000000..2b82df5c --- /dev/null +++ b/app/boards/shields/nice_view_adapter/nice_view_adapter.overlay @@ -0,0 +1,5 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ diff --git a/app/boards/shields/nice_view_adapter/nice_view_adapter.zmk.yml b/app/boards/shields/nice_view_adapter/nice_view_adapter.zmk.yml new file mode 100644 index 00000000..e63b01e5 --- /dev/null +++ b/app/boards/shields/nice_view_adapter/nice_view_adapter.zmk.yml @@ -0,0 +1,7 @@ +file_format: "1" +id: nice_view_adapter +name: nice!view adapter +type: shield +url: https://nicekeyboards.com/nice-view +requires: [i2c_oled] +exposes: [nice_view_header] diff --git a/app/core-coverage.yml b/app/core-coverage.yml index 90470ba3..12c03613 100644 --- a/app/core-coverage.yml +++ b/app/core-coverage.yml @@ -30,3 +30,6 @@ include: shield: romac_plus cmake-args: "-DCONFIG_ZMK_RGB_UNDERGLOW=y -DCONFIG_WS2812_STRIP=y" nickname: "underglow" +- board: nice_nano_v2 + shield: lily58_left nice_view_adapter nice_view + nickname: "niceview" diff --git a/app/drivers/gpio/CMakeLists.txt b/app/drivers/gpio/CMakeLists.txt index 0df31ad5..f4378151 100644 --- a/app/drivers/gpio/CMakeLists.txt +++ b/app/drivers/gpio/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2020 The ZMK Contributors +# Copyright (c) 2022 The ZMK Contributors # SPDX-License-Identifier: MIT zephyr_library_named(zmk__drivers__gpio) @@ -6,3 +6,4 @@ zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) zephyr_library_sources_ifdef(CONFIG_GPIO_595 gpio_595.c) zephyr_library_sources_ifdef(CONFIG_GPIO_MCP23017 gpio_mcp23017.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_MAX7318 gpio_max7318.c) diff --git a/app/drivers/gpio/Kconfig b/app/drivers/gpio/Kconfig index e6f8bfb8..43c7c24e 100644 --- a/app/drivers/gpio/Kconfig +++ b/app/drivers/gpio/Kconfig @@ -2,4 +2,5 @@ menuconfig ZMK_DRIVERS_GPIO bool "GPIO" rsource "Kconfig.mcp23017" -rsource "Kconfig.595" \ No newline at end of file +rsource "Kconfig.max7318" +rsource "Kconfig.595" diff --git a/app/drivers/gpio/Kconfig.max7318 b/app/drivers/gpio/Kconfig.max7318 new file mode 100644 index 00000000..ded7f926 --- /dev/null +++ b/app/drivers/gpio/Kconfig.max7318 @@ -0,0 +1,25 @@ +# MAX7318 GPIO configuration options + +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +DT_COMPAT_MAXIM_MAX7318 := maxim,max7318 + +menuconfig GPIO_MAX7318 + bool "MAX7318 I2C-based GPIO chip" + default $(dt_compat_enabled,$(DT_COMPAT_MAXIM_MAX7318)) + depends on I2C + select HAS_DTS_GPIO + select ZMK_DRIVERS_GPIO + help + Enable driver for MAX7318 I2C-based GPIO chip. + +if GPIO_MAX7318 + +config GPIO_MAX7318_INIT_PRIORITY + int "Init priority" + default 75 + help + Device driver initialization priority. + +endif #GPIO_MAX7318 diff --git a/app/drivers/gpio/gpio_max7318.c b/app/drivers/gpio/gpio_max7318.c new file mode 100644 index 00000000..3dcc710c --- /dev/null +++ b/app/drivers/gpio/gpio_max7318.c @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT maxim_max7318 + +/** + * @file Driver for MAX7318 I2C-based GPIO driver. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL +#include + +LOG_MODULE_REGISTER(gpio_max7318); + +// Register definitions +#define REG_INPUT_PORTA 0x00 +#define REG_INPUT_PORTB 0x01 +#define REG_OUTPUT_PORTA 0x02 +#define REG_OUTPUT_PORTB 0x03 +#define REG_IPOL_PORTA 0x04 +#define REG_IPOL_PORTB 0x05 +#define REG_CONFIG_PORTA 0x06 +#define REG_CONFIG_PORTB 0x07 + +// Configuration data +struct max7318_config { + struct gpio_driver_config common; + + struct i2c_dt_spec i2c_bus; + uint8_t ngpios; +}; + +// Runtime driver data +struct max7318_drv_data { + // gpio_driver_data needs to be first + struct gpio_driver_config data; + + struct k_sem lock; + + struct { + uint16_t ipol; + uint16_t config; + uint16_t output; + } reg_cache; +}; + +/** + * @brief Read the value of two consecutive registers + * + * Read two consecutive bytes from the register at address `reg` and `reg + 1`, + * typically reading from registers for port 0 and 1 simultaneously. + * + * @param dev The max7318 device. + * @param reg Register to read (the PORT0 of the pair of registers). + * @param buf Buffer to read data into. + * + * @return 0 if successful, failed otherwise. + */ +static int read_registers(const struct device *dev, uint8_t reg, uint16_t *buf) { + const struct max7318_config *config = dev->config; + + uint16_t data = 0; + int ret = i2c_burst_read_dt(&config->i2c_bus, reg, (uint8_t *)&data, sizeof(data)); + if (ret) { + LOG_DBG("i2c_write_read FAIL %d\n", ret); + return ret; + } + + *buf = sys_le16_to_cpu(data); + + LOG_DBG("max7318: read: reg[0x%X] = 0x%X, reg[0x%X] = 0x%X", reg, (*buf & 0xFF), (reg + 1), + (*buf >> 8)); + + return 0; +} + +/** + * @brief Write the value of two consecutive registers + * + * Write two consecutive bytes from the register at address `reg` and `reg + 1`, + * typically to registers for port 0 and 1 simultaneously. + * + * @param dev The max7318 device. + * @param reg Register to write (usually the register for PORT0). + * @param value The value to write + * + * @return 0 if successful, failed otherwise. + */ +static int write_registers(const struct device *dev, uint8_t reg, uint16_t value) { + const struct max7318_config *config = dev->config; + + LOG_DBG("max7318: write: reg[0x%X] = 0x%X, reg[0x%X] = 0x%X", reg, (value & 0xFF), (reg + 1), + (value >> 8)); + + uint16_t data = sys_cpu_to_le16(value); + + return i2c_burst_write_dt(&config->i2c_bus, reg, (uint8_t *)&data, sizeof(data)); +} + +/** + * @brief Setup the pin direction (input or output) + * + * @param dev The max7318 device. + * @param pin The pin number. + * @param flags Flags of pin or port. + * + * @return 0 if successful, failed otherwise + */ +static int set_pin_direction(const struct device *dev, uint32_t pin, int flags) { + struct max7318_drv_data *const drv_data = (struct max7318_drv_data *const)dev->data; + uint16_t *dir = &drv_data->reg_cache.config; + uint16_t *output = &drv_data->reg_cache.output; + + /* + The output register is 1=high, 0=low; the direction (config) register + is 1=input, 0=output. + */ + if ((flags & GPIO_OUTPUT) != 0U) { + if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0U) { + *output |= BIT(pin); + } else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0U) { + *output &= ~BIT(pin); + } + *dir &= ~BIT(pin); + } else { + *dir |= BIT(pin); + } + + int ret = write_registers(dev, REG_OUTPUT_PORTA, *output); + if (ret != 0) { + return ret; + } + + return write_registers(dev, REG_CONFIG_PORTA, *dir); +} + +/** + * @brief Setup the pin pull up/pull down status. This function doesn't actually set any + * registers, since the max7318 only supports a pullup, and it can't be controlled. + * + * @param dev The max7318 device. + * @param pin The pin number + * @param flags Flags of pin or port + * + * @return 0 if successful, failed otherwise + */ +static int set_pin_pull_direction(const struct device *dev, uint32_t pin, int flags) { + // actually, this chip only supports pull-up, and it can't be disabled. + // so, if we try to set anything else, return enotsup; we don't actually + // need to set any registers. + if ((flags & GPIO_PULL_DOWN) != 0U) { + return -ENOTSUP; + } + + return 0; +} + +static int max7318_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) { + struct max7318_drv_data *const drv_data = (struct max7318_drv_data *const)dev->data; + + /* Can't do I2C bus operations from an ISR */ + if (k_is_in_isr()) { + return -EWOULDBLOCK; + } + + k_sem_take(&drv_data->lock, K_FOREVER); + + int ret = 0; + if ((flags & GPIO_OPEN_DRAIN) != 0U) { + ret = -ENOTSUP; + goto done; + }; + + ret = set_pin_direction(dev, pin, flags); + if (ret != 0) { + LOG_ERR("error setting pin direction (%d)", ret); + goto done; + } + + ret = set_pin_pull_direction(dev, pin, flags); + if (ret != 0) { + LOG_ERR("error setting pin pull up/down (%d)", ret); + goto done; + } + +done: + k_sem_give(&drv_data->lock); + return ret; +} + +static int max7318_port_get_raw(const struct device *dev, uint32_t *value) { + struct max7318_drv_data *const drv_data = (struct max7318_drv_data *const)dev->data; + + /* Can't do I2C bus operations from an ISR */ + if (k_is_in_isr()) { + return -EWOULDBLOCK; + } + + k_sem_take(&drv_data->lock, K_FOREVER); + + uint16_t buf = 0; + int ret = read_registers(dev, REG_INPUT_PORTA, &buf); + if (ret != 0) { + goto done; + } + + *value = buf; + +done: + k_sem_give(&drv_data->lock); + return ret; +} + +static int max7318_port_set_masked_raw(const struct device *dev, uint32_t mask, uint32_t value) { + struct max7318_drv_data *const drv_data = (struct max7318_drv_data *const)dev->data; + + /* Can't do I2C bus operations from an ISR */ + if (k_is_in_isr()) { + return -EWOULDBLOCK; + } + + k_sem_take(&drv_data->lock, K_FOREVER); + + uint16_t buf = drv_data->reg_cache.output; + buf = (buf & ~mask) | (mask & value); + + int ret = write_registers(dev, REG_OUTPUT_PORTA, buf); + if (ret == 0) { + drv_data->reg_cache.output = buf; + } + + k_sem_give(&drv_data->lock); + return ret; +} + +static int max7318_port_set_bits_raw(const struct device *dev, uint32_t mask) { + return max7318_port_set_masked_raw(dev, mask, mask); +} + +static int max7318_port_clear_bits_raw(const struct device *dev, uint32_t mask) { + return max7318_port_set_masked_raw(dev, mask, 0); +} + +static int max7318_port_toggle_bits(const struct device *dev, uint32_t mask) { + struct max7318_drv_data *const drv_data = (struct max7318_drv_data *const)dev->data; + + /* Can't do I2C bus operations from an ISR */ + if (k_is_in_isr()) { + return -EWOULDBLOCK; + } + + k_sem_take(&drv_data->lock, K_FOREVER); + + uint16_t buf = drv_data->reg_cache.output; + buf ^= mask; + + int ret = write_registers(dev, REG_OUTPUT_PORTA, buf); + if (ret == 0) { + drv_data->reg_cache.output = buf; + } + + k_sem_give(&drv_data->lock); + return ret; +} + +static int max7318_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, + enum gpio_int_mode mode, enum gpio_int_trig trig) { + return -ENOTSUP; +} + +static const struct gpio_driver_api api_table = { + .pin_configure = max7318_config, + .port_get_raw = max7318_port_get_raw, + .port_set_masked_raw = max7318_port_set_masked_raw, + .port_set_bits_raw = max7318_port_set_bits_raw, + .port_clear_bits_raw = max7318_port_clear_bits_raw, + .port_toggle_bits = max7318_port_toggle_bits, + .pin_interrupt_configure = max7318_pin_interrupt_configure, +}; + +/** + * @brief Initialisation function of MAX7318 + * + * @param dev Device struct + * @return 0 if successful, failed otherwise. + */ +static int max7318_init(const struct device *dev) { + const struct max7318_config *const config = dev->config; + struct max7318_drv_data *const drv_data = (struct max7318_drv_data *const)dev->data; + + if (!device_is_ready(config->i2c_bus.bus)) { + LOG_WRN("i2c bus not ready!"); + return -EINVAL; + } + + LOG_INF("device initialised at 0x%x", config->i2c_bus.addr); + + k_sem_init(&drv_data->lock, 1, 1); + return 0; +} + +#define GPIO_PORT_PIN_MASK_FROM_NGPIOS(ngpios) ((gpio_port_pins_t)(((uint64_t)1 << (ngpios)) - 1U)) + +#define GPIO_PORT_PIN_MASK_FROM_DT_INST(inst) \ + GPIO_PORT_PIN_MASK_FROM_NGPIOS(DT_INST_PROP(inst, ngpios)) + +#define MAX7318_INIT(inst) \ + static struct max7318_config max7318_##inst##_config = { \ + .common = {.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst)}, \ + .i2c_bus = I2C_DT_SPEC_INST_GET(inst)}; \ + \ + static struct max7318_drv_data max7318_##inst##_drvdata = { \ + /* Default for registers according to datasheet */ \ + .reg_cache.ipol = 0x0, \ + .reg_cache.config = 0xFFFF, \ + .reg_cache.output = 0xFFFF, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, max7318_init, NULL, &max7318_##inst##_drvdata, \ + &max7318_##inst##_config, POST_KERNEL, \ + CONFIG_GPIO_MAX7318_INIT_PRIORITY, &api_table); + +DT_INST_FOREACH_STATUS_OKAY(MAX7318_INIT) diff --git a/app/drivers/kscan/Kconfig b/app/drivers/kscan/Kconfig index b76a27b4..51546006 100644 --- a/app/drivers/kscan/Kconfig +++ b/app/drivers/kscan/Kconfig @@ -32,6 +32,16 @@ config ZMK_KSCAN_GPIO_MATRIX if ZMK_KSCAN_GPIO_MATRIX +config ZMK_KSCAN_MATRIX_WAIT_BEFORE_INPUTS + int "Ticks to wait before reading inputs after an output set active" + default 0 + help + When iterating over each output to drive it active, read inputs, then set + inactive again, some boards may take time for output to propagate to the + inputs. In that scenario, set this value to a positive value to configure + the number of ticks to wait after setting an output active before reading + the inputs for their active state. + config ZMK_KSCAN_MATRIX_WAIT_BETWEEN_OUTPUTS int "Ticks to wait between each output when scanning" default 0 @@ -40,7 +50,7 @@ config ZMK_KSCAN_MATRIX_WAIT_BETWEEN_OUTPUTS inactive again, some boards may take time for the previous output to "settle" before reading inputs for the next active output column. In that scenario, set this value to a positive value to configure the number of - usecs to wait after reading each column of keys. + ticks to wait after reading each column of keys. endif # ZMK_KSCAN_GPIO_MATRIX diff --git a/app/drivers/kscan/kscan_gpio_matrix.c b/app/drivers/kscan/kscan_gpio_matrix.c index 1ab4d442..71fcad29 100644 --- a/app/drivers/kscan/kscan_gpio_matrix.c +++ b/app/drivers/kscan/kscan_gpio_matrix.c @@ -235,6 +235,10 @@ static int kscan_matrix_read(const struct device *dev) { return err; } +#if CONFIG_ZMK_KSCAN_MATRIX_WAIT_BEFORE_INPUTS > 0 + k_busy_wait(CONFIG_ZMK_KSCAN_MATRIX_WAIT_BEFORE_INPUTS); +#endif + for (int i = 0; i < config->inputs.len; i++) { const struct gpio_dt_spec *in_gpio = &config->inputs.gpios[i]; diff --git a/app/drivers/zephyr/dts/bindings/gpio/maxim,max7318.yaml b/app/drivers/zephyr/dts/bindings/gpio/maxim,max7318.yaml new file mode 100644 index 00000000..2db84bcd --- /dev/null +++ b/app/drivers/zephyr/dts/bindings/gpio/maxim,max7318.yaml @@ -0,0 +1,29 @@ +# +# Copyright (c) 2022 The ZMK Contributors +# +# SPDX-License-Identifier: MIT +# + +description: > + This is a representation of the Maxim MAX7318 I2C Gpio Expander. + +compatible: "maxim,max7318" + +include: [gpio-controller.yaml, i2c-device.yaml] + +properties: + label: + required: true + + "#gpio-cells": + const: 2 + + ngpios: + type: int + required: true + const: 16 + description: Number of gpios supported + +gpio-cells: + - pin + - flags diff --git a/app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml b/app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml index ed40f936..20235d04 100644 --- a/app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml +++ b/app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml @@ -14,3 +14,6 @@ properties: mods: type: int required: true + keep-mods: + type: int + required: false diff --git a/app/include/dt-bindings/zmk/keys.h b/app/include/dt-bindings/zmk/keys.h index 32e78795..d144ebb3 100644 --- a/app/include/dt-bindings/zmk/keys.h +++ b/app/include/dt-bindings/zmk/keys.h @@ -1404,6 +1404,10 @@ #define C_AC_DESKTOP_SHOW_ALL_WINDOWS \ (ZMK_HID_USAGE(HID_USAGE_CONSUMER, HID_USAGE_CONSUMER_AC_DESKTOP_SHOW_ALL_WINDOWS)) +/* Consumer AC Desktop Show All Applications */ +#define C_AC_DESKTOP_SHOW_ALL_APPLICATIONS \ + (ZMK_HID_USAGE(HID_USAGE_CONSUMER, HID_USAGE_CONSUMER_AC_DESKTOP_SHOW_ALL_APPLICATIONS)) + /* Consumer Keyboard Input Assist Previous */ #define C_KEYBOARD_INPUT_ASSIST_PREVIOUS \ (ZMK_HID_USAGE(HID_USAGE_CONSUMER, HID_USAGE_CONSUMER_KEYBOARD_INPUT_ASSIST_PREVIOUS)) diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h index 902b76d1..0f9cfe2e 100644 --- a/app/include/zmk/hid.h +++ b/app/include/zmk/hid.h @@ -135,6 +135,8 @@ int zmk_hid_register_mods(zmk_mod_flags_t explicit_modifiers); int zmk_hid_unregister_mods(zmk_mod_flags_t explicit_modifiers); int zmk_hid_implicit_modifiers_press(zmk_mod_flags_t implicit_modifiers); int zmk_hid_implicit_modifiers_release(); +int zmk_hid_masked_modifiers_set(zmk_mod_flags_t masked_modifiers); +int zmk_hid_masked_modifiers_clear(); int zmk_hid_keyboard_press(zmk_key_t key); int zmk_hid_keyboard_release(zmk_key_t key); diff --git a/app/src/backlight.c b/app/src/backlight.c index 28a93e73..159c6fb2 100644 --- a/app/src/backlight.c +++ b/app/src/backlight.c @@ -75,7 +75,7 @@ static void backlight_save_work_handler(struct k_work *work) { settings_save_one("backlight/state", &state, sizeof(state)); } -static K_DELAYED_WORK_DEFINE(backlight_save_work, backlight_save_work_handler); +static struct k_work_delayable backlight_save_work; #endif static int zmk_backlight_init(const struct device *_arg) { @@ -90,6 +90,7 @@ static int zmk_backlight_init(const struct device *_arg) { if (rc != 0) { LOG_ERR("Failed to load backlight settings: %d", rc); } + k_work_init_delayable(&backlight_save_work, backlight_save_work_handler); #endif #if IS_ENABLED(CONFIG_ZMK_BACKLIGHT_AUTO_OFF_USB) state.on = zmk_usb_is_powered(); @@ -104,8 +105,8 @@ static int zmk_backlight_update_and_save() { } #if IS_ENABLED(CONFIG_SETTINGS) - k_delayed_work_cancel(&backlight_save_work); - return k_delayed_work_submit(&backlight_save_work, K_MSEC(CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE)); + int ret = k_work_reschedule(&backlight_save_work, K_MSEC(CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE)); + return MIN(ret, 0); #else return 0; #endif diff --git a/app/src/behaviors/behavior_hold_tap.c b/app/src/behaviors/behavior_hold_tap.c index 413806b4..f09006ed 100644 --- a/app/src/behaviors/behavior_hold_tap.c +++ b/app/src/behaviors/behavior_hold_tap.c @@ -612,6 +612,10 @@ static int position_state_changed_listener(const zmk_event_t *eh) { decide_hold_tap(undecided_hold_tap, HT_TIMER_EVENT); } + if (undecided_hold_tap == NULL) { + return ZMK_EV_EVENT_BUBBLE; + } + if (!ev->state && find_captured_keydown_event(ev->position) == NULL) { // no keydown event has been captured, let it bubble. // we'll catch modifiers later in modifier_state_changed_listener diff --git a/app/src/behaviors/behavior_mod_morph.c b/app/src/behaviors/behavior_mod_morph.c index a40bd365..effbe4ac 100644 --- a/app/src/behaviors/behavior_mod_morph.c +++ b/app/src/behaviors/behavior_mod_morph.c @@ -27,6 +27,7 @@ struct behavior_mod_morph_config { struct zmk_behavior_binding normal_binding; struct zmk_behavior_binding morph_binding; zmk_mod_flags_t mods; + zmk_mod_flags_t masked_mods; }; struct behavior_mod_morph_data { @@ -45,6 +46,7 @@ static int on_mod_morph_binding_pressed(struct zmk_behavior_binding *binding, } if (zmk_hid_get_explicit_mods() & cfg->mods) { + zmk_hid_masked_modifiers_set(cfg->masked_mods); data->pressed_binding = (struct zmk_behavior_binding *)&cfg->morph_binding; } else { data->pressed_binding = (struct zmk_behavior_binding *)&cfg->normal_binding; @@ -64,7 +66,10 @@ static int on_mod_morph_binding_released(struct zmk_behavior_binding *binding, struct zmk_behavior_binding *pressed_binding = data->pressed_binding; data->pressed_binding = NULL; - return behavior_keymap_binding_released(pressed_binding, event); + int err; + err = behavior_keymap_binding_released(pressed_binding, event); + zmk_hid_masked_modifiers_clear(); + return err; } static const struct behavior_driver_api behavior_mod_morph_driver_api = { @@ -88,6 +93,8 @@ static int behavior_mod_morph_init(const struct device *dev) { return 0; } .normal_binding = _TRANSFORM_ENTRY(0, n), \ .morph_binding = _TRANSFORM_ENTRY(1, n), \ .mods = DT_INST_PROP(n, mods), \ + .masked_mods = COND_CODE_0(DT_INST_NODE_HAS_PROP(n, keep_mods), (DT_INST_PROP(n, mods)), \ + (DT_INST_PROP(n, mods) & ~DT_INST_PROP(n, keep_mods))), \ }; \ static struct behavior_mod_morph_data behavior_mod_morph_data_##n = {}; \ DEVICE_DT_INST_DEFINE(n, behavior_mod_morph_init, NULL, &behavior_mod_morph_data_##n, \ diff --git a/app/src/combo.c b/app/src/combo.c index 13ed1709..e434ae17 100644 --- a/app/src/combo.c +++ b/app/src/combo.c @@ -211,7 +211,7 @@ static int filter_timed_out_candidates(int64_t timestamp) { if (candidate->timeout_at > timestamp) { // reorder candidates so they're contiguous candidates[num_candidates].combo = candidate->combo; - candidates[num_candidates].timeout_at = candidates->timeout_at; + candidates[num_candidates].timeout_at = candidate->timeout_at; num_candidates++; } else { candidate->combo = NULL; diff --git a/app/src/event_manager.c b/app/src/event_manager.c index eef5d839..471432a8 100644 --- a/app/src/event_manager.c +++ b/app/src/event_manager.c @@ -25,6 +25,7 @@ int zmk_event_manager_handle_from(zmk_event_t *event, uint8_t start_index) { if (ev_sub->event_type != event->event) { continue; } + event->last_listener_index = i; ret = ev_sub->listener->callback(event); switch (ret) { case ZMK_EV_EVENT_BUBBLE: @@ -35,7 +36,6 @@ int zmk_event_manager_handle_from(zmk_event_t *event, uint8_t start_index) { goto release; case ZMK_EV_EVENT_CAPTURED: LOG_DBG("Listener captured the event"); - event->last_listener_index = i; // Listeners are expected to free events they capture return 0; default: diff --git a/app/src/hid.c b/app/src/hid.c index c3462dde..b66a910d 100644 --- a/app/src/hid.c +++ b/app/src/hid.c @@ -20,10 +20,12 @@ static struct zmk_hid_consumer_report consumer_report = {.report_id = 2, .body = // Only release the modifier if the count is 0. static int explicit_modifier_counts[8] = {0, 0, 0, 0, 0, 0, 0, 0}; static zmk_mod_flags_t explicit_modifiers = 0; +static zmk_mod_flags_t implicit_modifiers = 0; +static zmk_mod_flags_t masked_modifiers = 0; #define SET_MODIFIERS(mods) \ { \ - keyboard_report.body.modifiers = mods; \ + keyboard_report.body.modifiers = (mods & ~masked_modifiers) | implicit_modifiers; \ LOG_DBG("Modifiers set to 0x%02X", keyboard_report.body.modifiers); \ } @@ -158,13 +160,29 @@ static inline int check_keyboard_usage(zmk_key_t usage) { } \ } -int zmk_hid_implicit_modifiers_press(zmk_mod_flags_t implicit_modifiers) { +int zmk_hid_implicit_modifiers_press(zmk_mod_flags_t new_implicit_modifiers) { + implicit_modifiers = new_implicit_modifiers; zmk_mod_flags_t current = GET_MODIFIERS; - SET_MODIFIERS(explicit_modifiers | implicit_modifiers); + SET_MODIFIERS(explicit_modifiers); return current == GET_MODIFIERS ? 0 : 1; } int zmk_hid_implicit_modifiers_release() { + implicit_modifiers = 0; + zmk_mod_flags_t current = GET_MODIFIERS; + SET_MODIFIERS(explicit_modifiers); + return current == GET_MODIFIERS ? 0 : 1; +} + +int zmk_hid_masked_modifiers_set(zmk_mod_flags_t new_masked_modifiers) { + masked_modifiers = new_masked_modifiers; + zmk_mod_flags_t current = GET_MODIFIERS; + SET_MODIFIERS(explicit_modifiers); + return current == GET_MODIFIERS ? 0 : 1; +} + +int zmk_hid_masked_modifiers_clear() { + masked_modifiers = 0; zmk_mod_flags_t current = GET_MODIFIERS; SET_MODIFIERS(explicit_modifiers); return current == GET_MODIFIERS ? 0 : 1; diff --git a/app/src/usb.c b/app/src/usb.c index 5f170ee6..aa2d3e75 100644 --- a/app/src/usb.c +++ b/app/src/usb.c @@ -33,6 +33,7 @@ enum zmk_usb_conn_state zmk_usb_get_conn_state() { switch (usb_status) { case USB_DC_SUSPEND: case USB_DC_CONFIGURED: + case USB_DC_RESUME: return ZMK_USB_CONN_HID; case USB_DC_DISCONNECTED: diff --git a/app/tests/combo/combos-and-holdtaps-3/events.patterns b/app/tests/combo/combos-and-holdtaps-3/events.patterns new file mode 100644 index 00000000..833100f6 --- /dev/null +++ b/app/tests/combo/combos-and-holdtaps-3/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p \ No newline at end of file diff --git a/app/tests/combo/combos-and-holdtaps-3/keycode_events.snapshot b/app/tests/combo/combos-and-holdtaps-3/keycode_events.snapshot new file mode 100644 index 00000000..843832dd --- /dev/null +++ b/app/tests/combo/combos-and-holdtaps-3/keycode_events.snapshot @@ -0,0 +1,5 @@ +pressed: usage_page 0x07 keycode 0xE5 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/combo/combos-and-holdtaps-3/native_posix_64.keymap b/app/tests/combo/combos-and-holdtaps-3/native_posix_64.keymap new file mode 100644 index 00000000..d4053793 --- /dev/null +++ b/app/tests/combo/combos-and-holdtaps-3/native_posix_64.keymap @@ -0,0 +1,40 @@ +#include +#include +#include + +&mt { + flavor = "hold-preferred"; +}; + +/ { + combos { + compatible = "zmk,combos"; + combo_one { + timeout-ms = <40>; + key-positions = <0 1>; + bindings = <&kp X>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + label = "Default keymap"; + + default_layer { + bindings = < + &kp A &kp B + &mt RSHFT RET &kp C + >; + }; + }; +}; + +&kscan { + events = < + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_PRESS(1,1,10) + ZMK_MOCK_RELEASE(0,1,50) + ZMK_MOCK_RELEASE(1,1,50) + >; +}; diff --git a/app/tests/combo/combos-and-holdtaps-4/events.patterns b/app/tests/combo/combos-and-holdtaps-4/events.patterns new file mode 100644 index 00000000..833100f6 --- /dev/null +++ b/app/tests/combo/combos-and-holdtaps-4/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p \ No newline at end of file diff --git a/app/tests/combo/combos-and-holdtaps-4/keycode_events.snapshot b/app/tests/combo/combos-and-holdtaps-4/keycode_events.snapshot new file mode 100644 index 00000000..f84bc761 --- /dev/null +++ b/app/tests/combo/combos-and-holdtaps-4/keycode_events.snapshot @@ -0,0 +1,4 @@ +pressed: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/combo/combos-and-holdtaps-4/native_posix_64.keymap b/app/tests/combo/combos-and-holdtaps-4/native_posix_64.keymap new file mode 100644 index 00000000..ba6cecc6 --- /dev/null +++ b/app/tests/combo/combos-and-holdtaps-4/native_posix_64.keymap @@ -0,0 +1,46 @@ +#include +#include +#include + + +#define ZMK_COMBO(name, combo_bindings, keypos, combo_term) \ +/ { \ + combos { \ + compatible = "zmk,combos"; \ + combo_ ## name { \ + key-positions = ; \ + bindings = ; \ + timeout-ms = ; \ + }; \ + }; \ +}; + +ZMK_COMBO(qmark, &kp QMARK, 0 3, 30) +ZMK_COMBO(dllr, &kp DLLR, 1 3, 50) +ZMK_COMBO(tilde, &kp TILDE, 3 4, 50) + +/ { + keymap { + compatible = "zmk,keymap"; + label = "Default keymap"; + + default_layer { + bindings = < + &none &none + &kp A &mt LSHFT T + &none + >; + }; + }; +}; + +&kscan { + rows = <3>; + columns = <2>; + events = < + ZMK_MOCK_PRESS(1,1,500) + ZMK_MOCK_PRESS(1,0,100) + ZMK_MOCK_RELEASE(1,0,500) + ZMK_MOCK_RELEASE(1,1,0) + >; +}; \ No newline at end of file diff --git a/app/tests/mod-morph/1-no-morph/events.patterns b/app/tests/mod-morph/1-no-morph/events.patterns new file mode 100644 index 00000000..f1a41fcf --- /dev/null +++ b/app/tests/mod-morph/1-no-morph/events.patterns @@ -0,0 +1,8 @@ +s/.*hid_listener_keycode_pressed.*keycode/pressed: keycode/p +s/.*hid_listener_keycode_released.*keycode/released: keycode/p +s/.*hid_register_mod.*Modifiers set to /reg explicit: Modifiers set to /p +s/.*hid_unregister_mod.*Modifiers set to /unreg explicit: Modifiers set to /p +s/.*hid_implicit_modifiers_press.*Modifiers set to /reg implicit: Modifiers set to /p +s/.*hid_implicit_modifiers_release.*Modifiers set to /unreg implicit: Modifiers set to /p +s/.*hid_masked_modifiers_set.*Modifiers set to /mask mods: Modifiers set to /p +s/.*hid_masked_modifiers_clear.*Modifiers set to /unmask mods: Modifiers set to /p diff --git a/app/tests/mod-morph/1-no-morph/keycode_events.snapshot b/app/tests/mod-morph/1-no-morph/keycode_events.snapshot new file mode 100644 index 00000000..3a2d70fe --- /dev/null +++ b/app/tests/mod-morph/1-no-morph/keycode_events.snapshot @@ -0,0 +1,5 @@ +pressed: keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +reg implicit: Modifiers set to 0x00 +released: keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 +unreg implicit: Modifiers set to 0x00 +unmask mods: Modifiers set to 0x00 diff --git a/app/tests/mod-morph/1-no-morph/native_posix_64.keymap b/app/tests/mod-morph/1-no-morph/native_posix_64.keymap new file mode 100644 index 00000000..fb939de5 --- /dev/null +++ b/app/tests/mod-morph/1-no-morph/native_posix_64.keymap @@ -0,0 +1,11 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + >; +}; diff --git a/app/tests/mod-morph/2a-masked-morph/events.patterns b/app/tests/mod-morph/2a-masked-morph/events.patterns new file mode 100644 index 00000000..f1a41fcf --- /dev/null +++ b/app/tests/mod-morph/2a-masked-morph/events.patterns @@ -0,0 +1,8 @@ +s/.*hid_listener_keycode_pressed.*keycode/pressed: keycode/p +s/.*hid_listener_keycode_released.*keycode/released: keycode/p +s/.*hid_register_mod.*Modifiers set to /reg explicit: Modifiers set to /p +s/.*hid_unregister_mod.*Modifiers set to /unreg explicit: Modifiers set to /p +s/.*hid_implicit_modifiers_press.*Modifiers set to /reg implicit: Modifiers set to /p +s/.*hid_implicit_modifiers_release.*Modifiers set to /unreg implicit: Modifiers set to /p +s/.*hid_masked_modifiers_set.*Modifiers set to /mask mods: Modifiers set to /p +s/.*hid_masked_modifiers_clear.*Modifiers set to /unmask mods: Modifiers set to /p diff --git a/app/tests/mod-morph/2a-masked-morph/keycode_events.snapshot b/app/tests/mod-morph/2a-masked-morph/keycode_events.snapshot new file mode 100644 index 00000000..dcf2aae6 --- /dev/null +++ b/app/tests/mod-morph/2a-masked-morph/keycode_events.snapshot @@ -0,0 +1,12 @@ +pressed: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +reg explicit: Modifiers set to 0x02 +reg implicit: Modifiers set to 0x02 +mask mods: Modifiers set to 0x00 +pressed: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +reg implicit: Modifiers set to 0x00 +released: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +unreg implicit: Modifiers set to 0x00 +unmask mods: Modifiers set to 0x02 +released: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +unreg explicit: Modifiers set to 0x00 +unreg implicit: Modifiers set to 0x00 diff --git a/app/tests/mod-morph/2a-masked-morph/native_posix_64.keymap b/app/tests/mod-morph/2a-masked-morph/native_posix_64.keymap new file mode 100644 index 00000000..9ad50202 --- /dev/null +++ b/app/tests/mod-morph/2a-masked-morph/native_posix_64.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + ZMK_MOCK_RELEASE(1,0,10) + >; +}; diff --git a/app/tests/mod-morph/2b-masked-morph-implicit-overwrite/events.patterns b/app/tests/mod-morph/2b-masked-morph-implicit-overwrite/events.patterns new file mode 100644 index 00000000..f1a41fcf --- /dev/null +++ b/app/tests/mod-morph/2b-masked-morph-implicit-overwrite/events.patterns @@ -0,0 +1,8 @@ +s/.*hid_listener_keycode_pressed.*keycode/pressed: keycode/p +s/.*hid_listener_keycode_released.*keycode/released: keycode/p +s/.*hid_register_mod.*Modifiers set to /reg explicit: Modifiers set to /p +s/.*hid_unregister_mod.*Modifiers set to /unreg explicit: Modifiers set to /p +s/.*hid_implicit_modifiers_press.*Modifiers set to /reg implicit: Modifiers set to /p +s/.*hid_implicit_modifiers_release.*Modifiers set to /unreg implicit: Modifiers set to /p +s/.*hid_masked_modifiers_set.*Modifiers set to /mask mods: Modifiers set to /p +s/.*hid_masked_modifiers_clear.*Modifiers set to /unmask mods: Modifiers set to /p diff --git a/app/tests/mod-morph/2b-masked-morph-implicit-overwrite/keycode_events.snapshot b/app/tests/mod-morph/2b-masked-morph-implicit-overwrite/keycode_events.snapshot new file mode 100644 index 00000000..ce85f25d --- /dev/null +++ b/app/tests/mod-morph/2b-masked-morph-implicit-overwrite/keycode_events.snapshot @@ -0,0 +1,12 @@ +pressed: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +reg explicit: Modifiers set to 0x02 +reg implicit: Modifiers set to 0x02 +mask mods: Modifiers set to 0x00 +pressed: keycode 0x05 implicit_mods 0x02 explicit_mods 0x00 +reg implicit: Modifiers set to 0x02 +released: keycode 0x05 implicit_mods 0x02 explicit_mods 0x00 +unreg implicit: Modifiers set to 0x00 +unmask mods: Modifiers set to 0x02 +released: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +unreg explicit: Modifiers set to 0x00 +unreg implicit: Modifiers set to 0x00 diff --git a/app/tests/mod-morph/2b-masked-morph-implicit-overwrite/native_posix_64.keymap b/app/tests/mod-morph/2b-masked-morph-implicit-overwrite/native_posix_64.keymap new file mode 100644 index 00000000..74de8588 --- /dev/null +++ b/app/tests/mod-morph/2b-masked-morph-implicit-overwrite/native_posix_64.keymap @@ -0,0 +1,37 @@ +#include +#include +#include + +/ { + behaviors { + mod_morph: mod_morph { + compatible = "zmk,behavior-mod-morph"; + label = "MOD_MORPH_TEST"; + #binding-cells = <0>; + bindings = <&kp A>, <&kp LS(B)>; // implict mod overwrite + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &kp LEFT_ALT &mod_morph + &kp LEFT_SHIFT &kp RIGHT_SHIFT + >; + }; + }; +}; + +&kscan { + events = < + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + ZMK_MOCK_RELEASE(1,0,10) + >; +}; + diff --git a/app/tests/mod-morph/2c-masked-morph-and-explicit-mods/events.patterns b/app/tests/mod-morph/2c-masked-morph-and-explicit-mods/events.patterns new file mode 100644 index 00000000..f1a41fcf --- /dev/null +++ b/app/tests/mod-morph/2c-masked-morph-and-explicit-mods/events.patterns @@ -0,0 +1,8 @@ +s/.*hid_listener_keycode_pressed.*keycode/pressed: keycode/p +s/.*hid_listener_keycode_released.*keycode/released: keycode/p +s/.*hid_register_mod.*Modifiers set to /reg explicit: Modifiers set to /p +s/.*hid_unregister_mod.*Modifiers set to /unreg explicit: Modifiers set to /p +s/.*hid_implicit_modifiers_press.*Modifiers set to /reg implicit: Modifiers set to /p +s/.*hid_implicit_modifiers_release.*Modifiers set to /unreg implicit: Modifiers set to /p +s/.*hid_masked_modifiers_set.*Modifiers set to /mask mods: Modifiers set to /p +s/.*hid_masked_modifiers_clear.*Modifiers set to /unmask mods: Modifiers set to /p diff --git a/app/tests/mod-morph/2c-masked-morph-and-explicit-mods/keycode_events.snapshot b/app/tests/mod-morph/2c-masked-morph-and-explicit-mods/keycode_events.snapshot new file mode 100644 index 00000000..561f88a9 --- /dev/null +++ b/app/tests/mod-morph/2c-masked-morph-and-explicit-mods/keycode_events.snapshot @@ -0,0 +1,18 @@ +pressed: keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00 +reg explicit: Modifiers set to 0x04 +reg implicit: Modifiers set to 0x04 +pressed: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +reg explicit: Modifiers set to 0x06 +reg implicit: Modifiers set to 0x06 +mask mods: Modifiers set to 0x04 +pressed: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +reg implicit: Modifiers set to 0x04 +released: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +unreg implicit: Modifiers set to 0x04 +unmask mods: Modifiers set to 0x06 +released: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +unreg explicit: Modifiers set to 0x04 +unreg implicit: Modifiers set to 0x04 +released: keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00 +unreg explicit: Modifiers set to 0x00 +unreg implicit: Modifiers set to 0x00 diff --git a/app/tests/mod-morph/2c-masked-morph-and-explicit-mods/native_posix_64.keymap b/app/tests/mod-morph/2c-masked-morph-and-explicit-mods/native_posix_64.keymap new file mode 100644 index 00000000..d5406e19 --- /dev/null +++ b/app/tests/mod-morph/2c-masked-morph-and-explicit-mods/native_posix_64.keymap @@ -0,0 +1,15 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + ZMK_MOCK_RELEASE(1,0,10) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; diff --git a/app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/events.patterns b/app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/events.patterns new file mode 100644 index 00000000..f1a41fcf --- /dev/null +++ b/app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/events.patterns @@ -0,0 +1,8 @@ +s/.*hid_listener_keycode_pressed.*keycode/pressed: keycode/p +s/.*hid_listener_keycode_released.*keycode/released: keycode/p +s/.*hid_register_mod.*Modifiers set to /reg explicit: Modifiers set to /p +s/.*hid_unregister_mod.*Modifiers set to /unreg explicit: Modifiers set to /p +s/.*hid_implicit_modifiers_press.*Modifiers set to /reg implicit: Modifiers set to /p +s/.*hid_implicit_modifiers_release.*Modifiers set to /unreg implicit: Modifiers set to /p +s/.*hid_masked_modifiers_set.*Modifiers set to /mask mods: Modifiers set to /p +s/.*hid_masked_modifiers_clear.*Modifiers set to /unmask mods: Modifiers set to /p diff --git a/app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/keycode_events.snapshot b/app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/keycode_events.snapshot new file mode 100644 index 00000000..dcf2aae6 --- /dev/null +++ b/app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/keycode_events.snapshot @@ -0,0 +1,12 @@ +pressed: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +reg explicit: Modifiers set to 0x02 +reg implicit: Modifiers set to 0x02 +mask mods: Modifiers set to 0x00 +pressed: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +reg implicit: Modifiers set to 0x00 +released: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +unreg implicit: Modifiers set to 0x00 +unmask mods: Modifiers set to 0x02 +released: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +unreg explicit: Modifiers set to 0x00 +unreg implicit: Modifiers set to 0x00 diff --git a/app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/native_posix_64.keymap b/app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/native_posix_64.keymap new file mode 100644 index 00000000..7071e8cd --- /dev/null +++ b/app/tests/mod-morph/2d-masked-morph-into-hold-tap-tap/native_posix_64.keymap @@ -0,0 +1,45 @@ +#include +#include +#include + +&kscan { + events = < + /* Shift + tap &mod_morph --> expect B (but get Shift + B) */ + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; + +/ { + behaviors { + mod_morph: mod_morph { + compatible = "zmk,behavior-mod-morph"; + label = "MOD_MORPH_TEST"; + #binding-cells = <0>; + bindings = <&kp A>, << 1 B>; + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + + }; + + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &kp LEFT_SHIFT &mod_morph + &kp C &none + >; + }; + + second_layer { + bindings = < + &trans &trans + &kp D &trans + >; + }; + }; +}; diff --git a/app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/events.patterns b/app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/events.patterns new file mode 100644 index 00000000..f1a41fcf --- /dev/null +++ b/app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/events.patterns @@ -0,0 +1,8 @@ +s/.*hid_listener_keycode_pressed.*keycode/pressed: keycode/p +s/.*hid_listener_keycode_released.*keycode/released: keycode/p +s/.*hid_register_mod.*Modifiers set to /reg explicit: Modifiers set to /p +s/.*hid_unregister_mod.*Modifiers set to /unreg explicit: Modifiers set to /p +s/.*hid_implicit_modifiers_press.*Modifiers set to /reg implicit: Modifiers set to /p +s/.*hid_implicit_modifiers_release.*Modifiers set to /unreg implicit: Modifiers set to /p +s/.*hid_masked_modifiers_set.*Modifiers set to /mask mods: Modifiers set to /p +s/.*hid_masked_modifiers_clear.*Modifiers set to /unmask mods: Modifiers set to /p diff --git a/app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/keycode_events.snapshot b/app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/keycode_events.snapshot new file mode 100644 index 00000000..ba70ee98 --- /dev/null +++ b/app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/keycode_events.snapshot @@ -0,0 +1,12 @@ +pressed: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +reg explicit: Modifiers set to 0x02 +reg implicit: Modifiers set to 0x02 +mask mods: Modifiers set to 0x00 +pressed: keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +reg implicit: Modifiers set to 0x00 +released: keycode 0x07 implicit_mods 0x00 explicit_mods 0x00 +unreg implicit: Modifiers set to 0x00 +unmask mods: Modifiers set to 0x02 +released: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +unreg explicit: Modifiers set to 0x00 +unreg implicit: Modifiers set to 0x00 diff --git a/app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/native_posix_64.keymap b/app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/native_posix_64.keymap new file mode 100644 index 00000000..96c2f270 --- /dev/null +++ b/app/tests/mod-morph/2e-masked-morph-into-hold-tap-hold/native_posix_64.keymap @@ -0,0 +1,47 @@ +#include +#include +#include + +&kscan { + events = < + /* Shift + hold &mod_morph --> expect and get D (no shift) */ + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_PRESS(0,1,200) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,10) + ZMK_MOCK_RELEASE(0,1,10) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; + +/ { + behaviors { + mod_morph: mod_morph { + compatible = "zmk,behavior-mod-morph"; + label = "MOD_MORPH_TEST"; + #binding-cells = <0>; + bindings = <&kp A>, << 1 B>; + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + + }; + + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &kp LEFT_SHIFT &mod_morph + &kp C &none + >; + }; + + second_layer { + bindings = < + &trans &trans + &kp D &trans + >; + }; + }; +}; diff --git a/app/tests/mod-morph/3-unmasked-morph/events.patterns b/app/tests/mod-morph/3-unmasked-morph/events.patterns new file mode 100644 index 00000000..f1a41fcf --- /dev/null +++ b/app/tests/mod-morph/3-unmasked-morph/events.patterns @@ -0,0 +1,8 @@ +s/.*hid_listener_keycode_pressed.*keycode/pressed: keycode/p +s/.*hid_listener_keycode_released.*keycode/released: keycode/p +s/.*hid_register_mod.*Modifiers set to /reg explicit: Modifiers set to /p +s/.*hid_unregister_mod.*Modifiers set to /unreg explicit: Modifiers set to /p +s/.*hid_implicit_modifiers_press.*Modifiers set to /reg implicit: Modifiers set to /p +s/.*hid_implicit_modifiers_release.*Modifiers set to /unreg implicit: Modifiers set to /p +s/.*hid_masked_modifiers_set.*Modifiers set to /mask mods: Modifiers set to /p +s/.*hid_masked_modifiers_clear.*Modifiers set to /unmask mods: Modifiers set to /p diff --git a/app/tests/mod-morph/3-unmasked-morph/keycode_events.snapshot b/app/tests/mod-morph/3-unmasked-morph/keycode_events.snapshot new file mode 100644 index 00000000..424242d5 --- /dev/null +++ b/app/tests/mod-morph/3-unmasked-morph/keycode_events.snapshot @@ -0,0 +1,12 @@ +pressed: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +reg explicit: Modifiers set to 0x02 +reg implicit: Modifiers set to 0x02 +mask mods: Modifiers set to 0x02 +pressed: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +reg implicit: Modifiers set to 0x02 +released: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +unreg implicit: Modifiers set to 0x02 +unmask mods: Modifiers set to 0x02 +released: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00 +unreg explicit: Modifiers set to 0x00 +unreg implicit: Modifiers set to 0x00 diff --git a/app/tests/mod-morph/3-unmasked-morph/native_posix_64.keymap b/app/tests/mod-morph/3-unmasked-morph/native_posix_64.keymap new file mode 100644 index 00000000..a82d3ea7 --- /dev/null +++ b/app/tests/mod-morph/3-unmasked-morph/native_posix_64.keymap @@ -0,0 +1,37 @@ +#include +#include +#include + +/ { + behaviors { + mod_morph: mod_morph { + compatible = "zmk,behavior-mod-morph"; + label = "MOD_MORPH_TEST"; + #binding-cells = <0>; + bindings = <&kp A>, <&kp B>; + mods = <(MOD_LSFT|MOD_RSFT)>; + keep-mods = <(MOD_LSFT|MOD_RSFT)>; // no masking + }; + }; + + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &kp LEFT_ALT &mod_morph + &kp LEFT_SHIFT &kp RIGHT_SHIFT + >; + }; + }; +}; + +&kscan { + events = < + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_PRESS(0,1,10) + ZMK_MOCK_RELEASE(0,1,10) + ZMK_MOCK_RELEASE(1,0,10) + >; +}; diff --git a/app/tests/mod-morph/behavior_keymap.dtsi b/app/tests/mod-morph/behavior_keymap.dtsi new file mode 100644 index 00000000..09720d8d --- /dev/null +++ b/app/tests/mod-morph/behavior_keymap.dtsi @@ -0,0 +1,23 @@ +/ { + behaviors { + mod_morph: mod_morph { + compatible = "zmk,behavior-mod-morph"; + label = "MOD_MORPH_TEST"; + #binding-cells = <0>; + bindings = <&kp A>, <&kp B>; + mods = <(MOD_LSFT|MOD_RSFT)>; + }; + }; + + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &kp LEFT_ALT &mod_morph + &kp LEFT_SHIFT &kp RIGHT_SHIFT + >; + }; + }; +}; diff --git a/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/keycode_events.snapshot b/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/keycode_events.snapshot index 43edb961..45719679 100644 --- a/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/keycode_events.snapshot +++ b/app/tests/modifiers/mixed/kp-lctl-dn-mod-dn-lctl-up-mod-up/keycode_events.snapshot @@ -7,7 +7,7 @@ mods: Modifiers set to 0x03 released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00 unreg: Modifier 0 count: 0 unreg: Modifier 0 released -unreg: Modifiers set to 0x00 +unreg: Modifiers set to 0x02 mods: Modifiers set to 0x00 released: usage_page 0x07 keycode 0x05 implicit_mods 0x02 explicit_mods 0x00 mods: Modifiers set to 0x00 diff --git a/docs/docs/behaviors/hold-tap.md b/docs/docs/behaviors/hold-tap.md index 528b9aec..793e350d 100644 --- a/docs/docs/behaviors/hold-tap.md +++ b/docs/docs/behaviors/hold-tap.md @@ -28,7 +28,7 @@ We call this the 'hold-preferred' flavor of hold-taps. While this flavor may wor - The 'hold-preferred' flavor triggers the hold behavior when the `tapping-term-ms` has expired or another key is pressed. - The 'balanced' flavor will trigger the hold behavior when the `tapping-term-ms` has expired or another key is pressed and released. -- The 'tap-preferred' flavor triggers the hold behavior when the `tapping-term-ms` has expired. It triggers the tap behavior when another key is pressed. +- The 'tap-preferred' flavor triggers the hold behavior when the `tapping-term-ms` has expired. Pressing another key within `tapping-term-ms` does not affect the decision. - The 'tap-unless-interrupted' flavor triggers a hold behavior only when another key is pressed before `tapping-term-ms` has expired. It triggers the tap behavior in all other situations. When the hold-tap key is released and the hold behavior has not been triggered, the tap behavior will trigger. @@ -139,7 +139,7 @@ defaultValue="homerow_mods" values={[ {label: 'Homerow Mods', value: 'homerow_mods'}, {label: 'Autoshift', value: 'autoshift'}, -{label: 'Toggle-on-Tap, Momentary-on-Hold Layers', value: 'tog_mo'}, +{label: 'Toggle-on-Tap, Momentary-on-Hold Layers', value: 'mo_tog'}, ]}> @@ -276,25 +276,25 @@ A popular method of implementing Autoshift in ZMK involves a C-preprocessor macr - + -This hold-tap example implements a [toggle-layer](layers.md/#toggle-layer) when the keybind is tapped and a [momentary-layer](layers.md/#momentary-layer) when it is held. Similarly to the Autoshift and Sticky Hold use-cases, a `TOG_MO(layer)` macro is defined such that the `&tog` and `&mo` behaviors can target a single layer. +This hold-tap example implements a [momentary-layer](layers.md/#momentary-layer) when the keybind is held and a [toggle-layer](layers.md/#toggle-layer) when it is tapped. Similar to the Autoshift and Sticky Hold use-cases, a `MO_TOG(layer)` macro is defined such that the `&mo` and `&tog` behaviors can target a single layer. -```dtsi title="Hold-Tap Example: Toggle layer on Tap, Momentary layer on Hold" +```dtsi title="Hold-Tap Example: Momentary layer on Hold, Toggle layer on Tap" #include #include -#define TOG_MO(layer) &tog_mo layer layer // Macro to apply toggle-layer-on-tap/momentary-layer-on-hold to a specific layer +#define MO_TOG(layer) &mo_tog layer layer // Macro to apply momentary-layer-on-hold/toggle-layer-on-tap to a specific layer / { behaviors { - tog_mo: behavior_mo_tog { + mo_tog: behavior_mo_tog { compatible = "zmk,behavior-hold-tap"; label = "mo_tog"; #binding-cells = <2>; flavor = "hold-preferred"; tapping-term-ms = <200>; - bindings = <&tog>, <&mo>; + bindings = <&mo>, <&tog>; }; }; @@ -302,8 +302,8 @@ This hold-tap example implements a [toggle-layer](layers.md/#toggle-layer) when compatible = "zmk,keymap"; default_layer { bindings = < - &tog_mo 2 1 // &mo 2 on hold, &tog 1 on tap - TOG_MO(3) // &mo 3 on hold, &tog 3 on tap + &mo_tog 2 1 // &mo 2 on hold, &tog 1 on tap + MO_TOG(3) // &mo 3 on hold, &tog 3 on tap >; }; }; diff --git a/docs/docs/behaviors/mod-morph.md b/docs/docs/behaviors/mod-morph.md index 2606aaf8..33d18e33 100644 --- a/docs/docs/behaviors/mod-morph.md +++ b/docs/docs/behaviors/mod-morph.md @@ -14,8 +14,6 @@ The Mod-Morph behavior sends a different keypress, depending on whether a specif The Mod-Morph behavior acts as one of two keycodes, depending on if the required modifier is being held during the keypress. -When the modifier is being held it is sent along with the morphed keycode. This can cause problems when the morphed keycode and modifier have an existing relationship (such as `shift-delete` or `ctrl-v` on many operating systems). - ### Configuration An example of how to implement the mod-morph "Grave Escape": @@ -31,10 +29,6 @@ An example of how to implement the mod-morph "Grave Escape": mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>; }; }; - - keymap { - ... - }; }; ``` @@ -71,3 +65,26 @@ Example: ``` mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>; ``` + +### Advanced configuration + +`keep-mods` + +When a modifier specified in `mods` is being held, it won't be sent along with the morphed keycode unless it is also specified in `keep-mods`. By default `keep-mods` equals `0`, which means no modifier specified in `mods` will be sent along with the morphed keycode. + +For example, the following configuration morphs `LEFT_SHIFT` + `BACKSPACE` into `DELETE`, and morphs `RIGHT_SHIFT` + `BACKSPACE` into `RIGHT_SHIFT` + `DELETE`. + +``` +/ { + behaviors { + bspc_del: backspace_delete { + compatible = "zmk,behavior-mod-morph"; + label = "BACKSPACE_DELETE"; + #binding-cells = <0>; + bindings = <&kp BACKSPACE>, <&kp DELETE>; + mods = <(MOD_LSFT|MOD_RSFT)>; + keep-mods = <(MOD_RSFT)>; + }; + }; +}; +``` diff --git a/docs/docs/config/kscan.md b/docs/docs/config/kscan.md index 1f54692d..787d5136 100644 --- a/docs/docs/config/kscan.md +++ b/docs/docs/config/kscan.md @@ -93,9 +93,11 @@ Keyboard scan driver where keys are arranged on a matrix with one GPIO per row a Definition file: [zmk/app/drivers/kscan/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/app/drivers/kscan/Kconfig) -| Config | Type | Description | Default | -| --------------------------------- | ---- | ------------------------------------------------ | ------- | -| `CONFIG_ZMK_KSCAN_MATRIX_POLLING` | bool | Poll for key presses instead of using interrupts | n | +| Config | Type | Description | Default | +| ---------------------------------------------- | ----------- | ------------------------------------------------------------------------- | ------- | +| `CONFIG_ZMK_KSCAN_MATRIX_POLLING` | bool | Poll for key presses instead of using interrupts | n | +| `CONFIG_ZMK_KSCAN_MATRIX_WAIT_BEFORE_INPUTS` | int (ticks) | How long to wait before reading input pins after setting output active | 0 | +| `CONFIG_ZMK_KSCAN_MATRIX_WAIT_BETWEEN_OUTPUTS` | int (ticks) | How long to wait between each output to allow previous output to "settle" | 0 | ### Devicetree diff --git a/docs/docs/config/system.md b/docs/docs/config/system.md index 4784339c..a5347b96 100644 --- a/docs/docs/config/system.md +++ b/docs/docs/config/system.md @@ -15,7 +15,7 @@ Definition file: [zmk/app/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/ | Config | Type | Description | Default | | ------------------------------------ | ------ | ----------------------------------------------------------------------------- | ------- | -| `CONFIG_ZMK_KEYBOARD_NAME` | string | The name of the keyboard | | +| `CONFIG_ZMK_KEYBOARD_NAME` | string | The name of the keyboard (max 16 characters) | | | `CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE` | int | Milliseconds to wait after a setting change before writing it to flash memory | 60000 | | `CONFIG_ZMK_WPM` | bool | Enable calculating words per minute | n | | `CONFIG_HEAP_MEM_POOL_SIZE` | int | Size of the heap memory pool | 8192 | diff --git a/docs/docs/development/new-shield.md b/docs/docs/development/new-shield.md index 03a27289..565f424c 100644 --- a/docs/docs/development/new-shield.md +++ b/docs/docs/development/new-shield.md @@ -77,7 +77,7 @@ which controls the display name of the device over USB and BLE. The updated new default values should always be wrapped inside a conditional on the shield config name defined in the `Kconfig.shield` file. Here's the simplest example file. :::warning -Do not make the keyboard name too long, otherwise the bluetooth advertising might fail and you will not be able to find your keyboard from your laptop / tablet. +The keyboard name must be less than or equal to 16 characters in length, otherwise the bluetooth advertising might fail and you will not be able to find your keyboard from your device. ::: ``` diff --git a/docs/docs/features/keymaps.md b/docs/docs/features/keymaps.md index 59577d8f..6d4e5f2c 100644 --- a/docs/docs/features/keymaps.md +++ b/docs/docs/features/keymaps.md @@ -58,7 +58,7 @@ in the stack _also_ get the event. Binding a behavior at a certain key position may include up to two extra parameters that are used to alter the behavior when that specific key position is activated/deactivated. For example, when binding -the "key press" (`kp`) behavior at a certain key position, you must specific _which_ keycode should +the "key press" (`kp`) behavior at a certain key position, you must specify _which_ keycode should be used for that key position. ``` diff --git a/docs/docs/hardware.mdx b/docs/docs/hardware.mdx index d2b6aa9d..6f377073 100644 --- a/docs/docs/hardware.mdx +++ b/docs/docs/hardware.mdx @@ -21,13 +21,13 @@ export const toc = [ id: "composite", level: 2, }, - ...Object.values(groupedMetadata(Metadata).interconnects).map( - ({ interconnect }) => ({ + ...Object.values(groupedMetadata(Metadata).interconnects) + .filter((ic) => ic.interconnect !== undefined) + .map(({ interconnect }) => ({ value: `${interconnect.name} Interconnect`, id: interconnect.id, level: 3, - }) - ), + })), { value: "Other Hardware", id: "other-hardware", diff --git a/docs/package-lock.json b/docs/package-lock.json index 06a79af0..4297f7ac 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -3323,6 +3323,15 @@ "node": ">=6.0.0" } }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.11", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", @@ -8413,36 +8422,6 @@ "node": ">= 12" } }, - "node_modules/html-minifier-terser/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/html-minifier-terser/node_modules/terser": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz", - "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==", - "dependencies": { - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/html-minifier-terser/node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, "node_modules/html-tags": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", @@ -13561,12 +13540,13 @@ } }, "node_modules/terser": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.9.0.tgz", - "integrity": "sha512-h5hxa23sCdpzcye/7b8YqbE5OwKca/ni0RQz1uRX3tGh8haaGHqcuSqbGRybuAKNdntZ0mDgFNXPJ48xQ2RXKQ==", + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", "commander": "^2.20.0", - "source-map": "~0.7.2", "source-map-support": "~0.5.20" }, "bin": { @@ -13614,14 +13594,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, - "node_modules/terser/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "engines": { - "node": ">= 8" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -17436,6 +17408,15 @@ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.1.tgz", "integrity": "sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==" }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, "@jridgewell/sourcemap-codec": { "version": "1.4.11", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", @@ -21282,29 +21263,6 @@ "version": "8.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" - }, - "terser": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz", - "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==", - "requires": { - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - } - } } } }, @@ -25015,12 +24973,13 @@ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" }, "terser": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.9.0.tgz", - "integrity": "sha512-h5hxa23sCdpzcye/7b8YqbE5OwKca/ni0RQz1uRX3tGh8haaGHqcuSqbGRybuAKNdntZ0mDgFNXPJ48xQ2RXKQ==", + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", "commander": "^2.20.0", - "source-map": "~0.7.2", "source-map-support": "~0.5.20" }, "dependencies": { @@ -25028,11 +24987,6 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" } } }, diff --git a/docs/src/components/hardware-utils.ts b/docs/src/components/hardware-utils.ts index 13ca5eb6..76452dc3 100644 --- a/docs/src/components/hardware-utils.ts +++ b/docs/src/components/hardware-utils.ts @@ -41,7 +41,11 @@ function groupedShield(agg: GroupedMetadata, shield: Shield) { ic.shields.push(shield); agg.interconnects[id] = ic; }); - + shield.exposes?.forEach((id) => { + let ic = agg.interconnects[id] ?? { boards: [], shields: [] }; + ic.shields.push(shield); + agg.interconnects[id] = ic; + }); return agg; } diff --git a/docs/src/data/groups.js b/docs/src/data/groups.js index f7ced161..0eb15d27 100644 --- a/docs/src/data/groups.js +++ b/docs/src/data/groups.js @@ -47,6 +47,7 @@ export default { "C_AC_DEL", "C_AC_VIEW_TOGGLE", "C_AC_DESKTOP_SHOW_ALL_WINDOWS", + "C_AC_DESKTOP_SHOW_ALL_APPLICATIONS", "C_VOICE_COMMAND", ], applications: [ diff --git a/docs/src/data/hid.js b/docs/src/data/hid.js index f2bde565..dde8bae4 100644 --- a/docs/src/data/hid.js +++ b/docs/src/data/hid.js @@ -2732,7 +2732,7 @@ export default [ ], documentation: "https://usb.org/sites/default/files/hut1_2.pdf#page=86", os: { - windows: null, + windows: true, linux: true, android: false, macos: true, @@ -2753,7 +2753,7 @@ export default [ ], documentation: "https://usb.org/sites/default/files/hut1_2.pdf#page=86", os: { - windows: null, + windows: true, linux: true, android: false, macos: true, @@ -2774,7 +2774,7 @@ export default [ ], documentation: "https://usb.org/sites/default/files/hut1_2.pdf#page=86", os: { - windows: null, + windows: true, linux: true, android: false, macos: true, @@ -2795,7 +2795,7 @@ export default [ ], documentation: "https://usb.org/sites/default/files/hut1_2.pdf#page=86", os: { - windows: null, + windows: true, linux: true, android: false, macos: true, @@ -2816,7 +2816,7 @@ export default [ ], documentation: "https://usb.org/sites/default/files/hut1_2.pdf#page=86", os: { - windows: null, + windows: true, linux: true, android: false, macos: true, @@ -2837,7 +2837,7 @@ export default [ ], documentation: "https://usb.org/sites/default/files/hut1_2.pdf#page=86", os: { - windows: null, + windows: true, linux: true, android: false, macos: true, @@ -2858,7 +2858,7 @@ export default [ ], documentation: "https://usb.org/sites/default/files/hut1_2.pdf#page=86", os: { - windows: null, + windows: true, linux: true, android: false, macos: true, @@ -2879,7 +2879,7 @@ export default [ ], documentation: "https://usb.org/sites/default/files/hut1_2.pdf#page=86", os: { - windows: null, + windows: true, linux: true, android: false, macos: true, @@ -2900,7 +2900,7 @@ export default [ ], documentation: "https://usb.org/sites/default/files/hut1_2.pdf#page=86", os: { - windows: null, + windows: true, linux: true, android: false, macos: true, @@ -2921,7 +2921,7 @@ export default [ ], documentation: "https://usb.org/sites/default/files/hut1_2.pdf#page=86", os: { - windows: null, + windows: true, linux: true, android: false, macos: true, @@ -2942,7 +2942,7 @@ export default [ ], documentation: "https://usb.org/sites/default/files/hut1_2.pdf#page=86", os: { - windows: null, + windows: true, linux: true, android: false, macos: true, @@ -2963,7 +2963,7 @@ export default [ ], documentation: "https://usb.org/sites/default/files/hut1_2.pdf#page=86", os: { - windows: null, + windows: true, linux: true, android: false, macos: true, @@ -7251,7 +7251,7 @@ export default [ windows: null, linux: true, android: true, - macos: null, + macos: true, ios: null, }, footnotes: {}, @@ -7713,7 +7713,28 @@ export default [ windows: null, linux: true, android: null, - macos: null, + macos: true, + ios: null, + }, + footnotes: {}, + }, + { + names: ["C_AC_DESKTOP_SHOW_ALL_APPLICATIONS"], + description: "Desktop Show All Applications", + context: "Consumer AC", + clarify: true, + usages: [ + { + application: consumerApplication, + item: usage(consumerPage, 0x2a2), + }, + ], + documentation: "https://usb.org/sites/default/files/hut1_2.pdf#page=153", + os: { + windows: null, + linux: true, + android: null, + macos: true, ios: null, }, footnotes: {}, diff --git a/docs/src/data/power.js b/docs/src/data/power.js index 28e4b0ed..354f0511 100644 --- a/docs/src/data/power.js +++ b/docs/src/data/power.js @@ -96,4 +96,10 @@ export const displayPower = { active: 10000, // Estimated power draw when about half the pixels are on sleep: 7, // Deep sleep power draw (display off) }, + // Based on the nice!view using Sharp's LS011B7DH01 + NICEVIEW: { + activePercent: 0.01, // Estimated two refreshes per second taking five milliseconds each + active: 1425, // Power draw during refresh (225uA display + 1200uA SPIM) + sleep: 1, // Idle power draw of the display + }, }; diff --git a/docs/src/pages/power-profiler.js b/docs/src/pages/power-profiler.js index c2302570..c909ec02 100644 --- a/docs/src/pages/power-profiler.js +++ b/docs/src/pages/power-profiler.js @@ -308,6 +308,7 @@ function PowerProfiler() { + )}