Merge remote-tracking branch 'upstream/main' into conditional_mod_tap
This commit is contained in:
commit
c63cde87fd
111 changed files with 11883 additions and 6978 deletions
|
@ -1,4 +1,4 @@
|
||||||
FROM zmkfirmware/zmk-dev-arm:2.4
|
FROM zmkfirmware/zmk-dev-arm:2.5
|
||||||
|
|
||||||
COPY .bashrc tmp
|
COPY .bashrc tmp
|
||||||
RUN mv /tmp/.bashrc ~/.bashrc
|
RUN mv /tmp/.bashrc ~/.bashrc
|
||||||
|
|
7
.github/workflows/build.yml
vendored
7
.github/workflows/build.yml
vendored
|
@ -14,12 +14,13 @@ jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: zmkfirmware/zmk-build-arm:2.4
|
image: zmkfirmware/zmk-build-arm:2.5
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
board:
|
board:
|
||||||
- bluemicro840_v1
|
- bluemicro840_v1
|
||||||
- nice_nano
|
- nice_nano
|
||||||
|
- nice_nano_v2
|
||||||
- nrfmicro_13
|
- nrfmicro_13
|
||||||
- proton_c
|
- proton_c
|
||||||
shield:
|
shield:
|
||||||
|
@ -69,11 +70,11 @@ jobs:
|
||||||
- board: planck_rev6
|
- board: planck_rev6
|
||||||
- board: proton_c
|
- board: proton_c
|
||||||
shield: clueboard_california
|
shield: clueboard_california
|
||||||
- board: nice_nano
|
- board: nice_nano_v2
|
||||||
shield: kyria_left
|
shield: kyria_left
|
||||||
cmake-args: -DCONFIG_ZMK_DISPLAY=y
|
cmake-args: -DCONFIG_ZMK_DISPLAY=y
|
||||||
skip-archive: true
|
skip-archive: true
|
||||||
- board: nice_nano
|
- board: nice_nano_v2
|
||||||
shield: kyria_right
|
shield: kyria_right
|
||||||
cmake-args: -DCONFIG_ZMK_DISPLAY=y
|
cmake-args: -DCONFIG_ZMK_DISPLAY=y
|
||||||
skip-archive: true
|
skip-archive: true
|
||||||
|
|
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
@ -16,7 +16,7 @@ jobs:
|
||||||
integration_test:
|
integration_test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: zmkfirmware/zmk-build-arm:2.4
|
image: zmkfirmware/zmk-build-arm:2.5
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
|
@ -91,7 +91,7 @@ You can setup git to run prettier automatically when you commit by installing th
|
||||||
### Development Setup
|
### Development Setup
|
||||||
|
|
||||||
To get your development environment setup going, start at the
|
To get your development environment setup going, start at the
|
||||||
[basic setup](https://zmk.dev/docs/dev-setup) docs, and make sure you can build and flash
|
[basic setup](https://zmk.dev/docs/development/setup/) docs, and make sure you can build and flash
|
||||||
your own locally built firmware.
|
your own locally built firmware.
|
||||||
|
|
||||||
### Formatting
|
### Formatting
|
||||||
|
|
|
@ -36,6 +36,7 @@ target_sources(app PRIVATE src/events/position_state_changed.c)
|
||||||
target_sources(app PRIVATE src/events/layer_state_changed.c)
|
target_sources(app PRIVATE src/events/layer_state_changed.c)
|
||||||
target_sources(app PRIVATE src/events/keycode_state_changed.c)
|
target_sources(app PRIVATE src/events/keycode_state_changed.c)
|
||||||
target_sources(app PRIVATE src/events/modifiers_state_changed.c)
|
target_sources(app PRIVATE src/events/modifiers_state_changed.c)
|
||||||
|
target_sources(app PRIVATE src/events/endpoint_selection_changed.c)
|
||||||
target_sources(app PRIVATE src/events/sensor_event.c)
|
target_sources(app PRIVATE src/events/sensor_event.c)
|
||||||
target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/events/wpm_state_changed.c)
|
target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/events/wpm_state_changed.c)
|
||||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/ble_active_profile_changed.c)
|
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/ble_active_profile_changed.c)
|
||||||
|
|
12
app/Kconfig
12
app/Kconfig
|
@ -263,14 +263,11 @@ config ZMK_SLEEP
|
||||||
|
|
||||||
if ZMK_SLEEP
|
if ZMK_SLEEP
|
||||||
|
|
||||||
config SYS_POWER_DEEP_SLEEP_STATES
|
|
||||||
default y
|
|
||||||
|
|
||||||
choice SYS_PM_POLICY
|
choice SYS_PM_POLICY
|
||||||
default SYS_PM_POLICY_APP
|
default PM_POLICY_APP
|
||||||
endchoice
|
endchoice
|
||||||
|
|
||||||
config DEVICE_POWER_MANAGEMENT
|
config PM_DEVICE
|
||||||
default y
|
default y
|
||||||
|
|
||||||
config ZMK_IDLE_SLEEP_TIMEOUT
|
config ZMK_IDLE_SLEEP_TIMEOUT
|
||||||
|
@ -422,6 +419,11 @@ config ZMK_WPM
|
||||||
config SENSOR
|
config SENSOR
|
||||||
default y
|
default y
|
||||||
|
|
||||||
|
choice CBPRINTF_IMPLEMENTATION
|
||||||
|
default CBPRINTF_NANO
|
||||||
|
|
||||||
|
endchoice
|
||||||
|
|
||||||
module = ZMK
|
module = ZMK
|
||||||
module-str = zmk
|
module-str = zmk
|
||||||
source "subsys/logging/Kconfig.template.log_config"
|
source "subsys/logging/Kconfig.template.log_config"
|
||||||
|
|
|
@ -11,6 +11,9 @@ CONFIG_FPU=y
|
||||||
# enable GPIO
|
# enable GPIO
|
||||||
CONFIG_GPIO=y
|
CONFIG_GPIO=y
|
||||||
|
|
||||||
|
# Enable pinmux
|
||||||
|
CONFIG_PINMUX=y
|
||||||
|
|
||||||
# Needed to reduce this to size that will fit on F072
|
# Needed to reduce this to size that will fit on F072
|
||||||
CONFIG_HEAP_MEM_POOL_SIZE=1024
|
CONFIG_HEAP_MEM_POOL_SIZE=1024
|
||||||
|
|
||||||
|
|
|
@ -4,4 +4,4 @@ config BOARD_ENABLE_DCDC
|
||||||
bool "Enable DCDC mode"
|
bool "Enable DCDC mode"
|
||||||
select SOC_DCDC_NRF52X
|
select SOC_DCDC_NRF52X
|
||||||
default y
|
default y
|
||||||
depends on BOARD_NICE_NANO
|
depends on (BOARD_NICE_NANO || BOARD_NICE_NANO_V2)
|
||||||
|
|
|
@ -7,3 +7,7 @@ config BOARD_NICE_NANO
|
||||||
bool "nice!nano"
|
bool "nice!nano"
|
||||||
depends on SOC_NRF52840_QIAA
|
depends on SOC_NRF52840_QIAA
|
||||||
|
|
||||||
|
config BOARD_NICE_NANO_V2
|
||||||
|
bool "nice!nano v2"
|
||||||
|
depends on SOC_NRF52840_QIAA
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Copyright (c) 2020 Pete Johanson
|
# Copyright (c) 2021 The ZMK Contributors
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
if BOARD_NICE_NANO
|
if BOARD_NICE_NANO || BOARD_NICE_NANO_V2
|
||||||
|
|
||||||
config BOARD
|
config BOARD
|
||||||
default "nice_nano"
|
default "nice_nano"
|
||||||
|
@ -25,7 +25,18 @@ config ZMK_BLE
|
||||||
config ZMK_USB
|
config ZMK_USB
|
||||||
default y
|
default y
|
||||||
|
|
||||||
|
endif # BOARD_NICE_NANO || BOARD_NICE_NANO_V2
|
||||||
|
|
||||||
|
if BOARD_NICE_NANO
|
||||||
|
|
||||||
config ZMK_BATTERY_VOLTAGE_DIVIDER
|
config ZMK_BATTERY_VOLTAGE_DIVIDER
|
||||||
default y
|
default y
|
||||||
|
|
||||||
endif # BOARD_NICE_NANO
|
endif # BOARD_NICE_NANO
|
||||||
|
|
||||||
|
if BOARD_NICE_NANO_V2
|
||||||
|
|
||||||
|
config ZMK_BATTERY_NRF_VDDH
|
||||||
|
default y
|
||||||
|
|
||||||
|
endif # BOARD_NICE_NANO_V2
|
||||||
|
|
|
@ -1,31 +1,13 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Pete Johanson
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/dts-v1/;
|
/dts-v1/;
|
||||||
#include <nordic/nrf52840_qiaa.dtsi>
|
#include "nice_nano.dtsi"
|
||||||
#include "arduino_pro_micro_pins.dtsi"
|
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
model = "nice!nano";
|
|
||||||
compatible = "nice,nano";
|
|
||||||
|
|
||||||
chosen {
|
|
||||||
zephyr,code-partition = &code_partition;
|
|
||||||
zephyr,sram = &sram0;
|
|
||||||
zephyr,flash = &flash0;
|
|
||||||
};
|
|
||||||
|
|
||||||
leds {
|
|
||||||
compatible = "gpio-leds";
|
|
||||||
blue_led: led_0 {
|
|
||||||
gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>;
|
|
||||||
label = "Blue LED";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
ext-power {
|
ext-power {
|
||||||
compatible = "zmk,ext-power-generic";
|
compatible = "zmk,ext-power-generic";
|
||||||
label = "EXT_POWER";
|
label = "EXT_POWER";
|
||||||
|
@ -40,76 +22,3 @@
|
||||||
full-ohms = <(2000000 + 806000)>;
|
full-ohms = <(2000000 + 806000)>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
&adc {
|
|
||||||
status = "okay";
|
|
||||||
};
|
|
||||||
|
|
||||||
&gpiote {
|
|
||||||
status = "okay";
|
|
||||||
};
|
|
||||||
|
|
||||||
&gpio0 {
|
|
||||||
status = "okay";
|
|
||||||
};
|
|
||||||
|
|
||||||
&gpio1 {
|
|
||||||
status = "okay";
|
|
||||||
};
|
|
||||||
|
|
||||||
&i2c0 {
|
|
||||||
compatible = "nordic,nrf-twi";
|
|
||||||
sda-pin = <17>;
|
|
||||||
scl-pin = <20>;
|
|
||||||
};
|
|
||||||
|
|
||||||
&uart0 {
|
|
||||||
compatible = "nordic,nrf-uarte";
|
|
||||||
tx-pin = <6>;
|
|
||||||
rx-pin = <8>;
|
|
||||||
};
|
|
||||||
|
|
||||||
&usbd {
|
|
||||||
status = "okay";
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
&flash0 {
|
|
||||||
/*
|
|
||||||
* For more information, see:
|
|
||||||
* http://docs.zephyrproject.org/latest/devices/dts/flash_partitions.html
|
|
||||||
*/
|
|
||||||
partitions {
|
|
||||||
compatible = "fixed-partitions";
|
|
||||||
#address-cells = <1>;
|
|
||||||
#size-cells = <1>;
|
|
||||||
|
|
||||||
sd_partition: partition@0 {
|
|
||||||
label = "softdevice";
|
|
||||||
reg = <0x00000000 0x00026000>;
|
|
||||||
};
|
|
||||||
code_partition: partition@26000 {
|
|
||||||
label = "code_partition";
|
|
||||||
reg = <0x00026000 0x000c6000>;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The flash starting at 0x000ec000 and ending at
|
|
||||||
* 0x000f3fff is reserved for use by the application.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Storage partition will be used by FCB/LittleFS/NVS
|
|
||||||
* if enabled.
|
|
||||||
*/
|
|
||||||
storage_partition: partition@ec000 {
|
|
||||||
label = "storage";
|
|
||||||
reg = <0x000ec000 0x00008000>;
|
|
||||||
};
|
|
||||||
|
|
||||||
boot_partition: partition@f4000 {
|
|
||||||
label = "adafruit_boot";
|
|
||||||
reg = <0x000f4000 0x0000c000>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
100
app/boards/arm/nice_nano/nice_nano.dtsi
Normal file
100
app/boards/arm/nice_nano/nice_nano.dtsi
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <nordic/nrf52840_qiaa.dtsi>
|
||||||
|
#include "arduino_pro_micro_pins.dtsi"
|
||||||
|
|
||||||
|
/ {
|
||||||
|
model = "nice!nano";
|
||||||
|
compatible = "nice,nano";
|
||||||
|
|
||||||
|
chosen {
|
||||||
|
zephyr,code-partition = &code_partition;
|
||||||
|
zephyr,sram = &sram0;
|
||||||
|
zephyr,flash = &flash0;
|
||||||
|
};
|
||||||
|
|
||||||
|
leds {
|
||||||
|
compatible = "gpio-leds";
|
||||||
|
blue_led: led_0 {
|
||||||
|
gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>;
|
||||||
|
label = "Blue LED";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
&adc {
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
|
&gpiote {
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
|
&gpio0 {
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
|
&gpio1 {
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
|
&i2c0 {
|
||||||
|
compatible = "nordic,nrf-twi";
|
||||||
|
sda-pin = <17>;
|
||||||
|
scl-pin = <20>;
|
||||||
|
};
|
||||||
|
|
||||||
|
&uart0 {
|
||||||
|
compatible = "nordic,nrf-uarte";
|
||||||
|
tx-pin = <6>;
|
||||||
|
rx-pin = <8>;
|
||||||
|
};
|
||||||
|
|
||||||
|
&usbd {
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
&flash0 {
|
||||||
|
/*
|
||||||
|
* For more information, see:
|
||||||
|
* http://docs.zephyrproject.org/latest/devices/dts/flash_partitions.html
|
||||||
|
*/
|
||||||
|
partitions {
|
||||||
|
compatible = "fixed-partitions";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
|
||||||
|
sd_partition: partition@0 {
|
||||||
|
label = "softdevice";
|
||||||
|
reg = <0x00000000 0x00026000>;
|
||||||
|
};
|
||||||
|
code_partition: partition@26000 {
|
||||||
|
label = "code_partition";
|
||||||
|
reg = <0x00026000 0x000c6000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The flash starting at 0x000ec000 and ending at
|
||||||
|
* 0x000f3fff is reserved for use by the application.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Storage partition will be used by FCB/LittleFS/NVS
|
||||||
|
* if enabled.
|
||||||
|
*/
|
||||||
|
storage_partition: partition@ec000 {
|
||||||
|
label = "storage";
|
||||||
|
reg = <0x000ec000 0x00008000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
boot_partition: partition@f4000 {
|
||||||
|
label = "adafruit_boot";
|
||||||
|
reg = <0x000f4000 0x0000c000>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
22
app/boards/arm/nice_nano/nice_nano_v2.dts
Normal file
22
app/boards/arm/nice_nano/nice_nano_v2.dts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/dts-v1/;
|
||||||
|
#include "nice_nano.dtsi"
|
||||||
|
|
||||||
|
/ {
|
||||||
|
ext-power {
|
||||||
|
compatible = "zmk,ext-power-generic";
|
||||||
|
label = "EXT_POWER";
|
||||||
|
control-gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>;
|
||||||
|
init-delay-ms = <10>;
|
||||||
|
};
|
||||||
|
|
||||||
|
vbatt {
|
||||||
|
compatible = "zmk,battery-nrf-vddh";
|
||||||
|
label = "BATTERY";
|
||||||
|
};
|
||||||
|
};
|
15
app/boards/arm/nice_nano/nice_nano_v2.yaml
Normal file
15
app/boards/arm/nice_nano/nice_nano_v2.yaml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
identifier: nice_nano_v2
|
||||||
|
name: nice!nano v2
|
||||||
|
type: mcu
|
||||||
|
arch: arm
|
||||||
|
toolchain:
|
||||||
|
- zephyr
|
||||||
|
- gnuarmemb
|
||||||
|
- xtools
|
||||||
|
supported:
|
||||||
|
- adc
|
||||||
|
- usb_device
|
||||||
|
- ble
|
||||||
|
- ieee802154
|
||||||
|
- pwm
|
||||||
|
- watchdog
|
20
app/boards/arm/nice_nano/nice_nano_v2_defconfig
Normal file
20
app/boards/arm/nice_nano/nice_nano_v2_defconfig
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
CONFIG_SOC_SERIES_NRF52X=y
|
||||||
|
CONFIG_SOC_NRF52840_QIAA=y
|
||||||
|
CONFIG_BOARD_NICE_NANO_V2=y
|
||||||
|
|
||||||
|
# Enable MPU
|
||||||
|
CONFIG_ARM_MPU=y
|
||||||
|
|
||||||
|
# enable GPIO
|
||||||
|
CONFIG_GPIO=y
|
||||||
|
|
||||||
|
CONFIG_USE_DT_CODE_PARTITION=y
|
||||||
|
|
||||||
|
CONFIG_MPU_ALLOW_FLASH_WRITE=y
|
||||||
|
CONFIG_NVS=y
|
||||||
|
CONFIG_SETTINGS_NVS=y
|
||||||
|
CONFIG_FLASH=y
|
||||||
|
CONFIG_FLASH_PAGE_LAYOUT=y
|
||||||
|
CONFIG_FLASH_MAP=y
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
/dts-v1/;
|
/dts-v1/;
|
||||||
#include <st/f3/stm32f303Xc.dtsi>
|
#include <st/f3/stm32f303Xc.dtsi>
|
||||||
|
#include <dt-bindings/zmk/matrix_transform.h>
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
model = "Plack PCD, rev6";
|
model = "Plack PCD, rev6";
|
||||||
|
@ -15,11 +16,13 @@
|
||||||
zephyr,sram = &sram0;
|
zephyr,sram = &sram0;
|
||||||
zephyr,flash = &flash0;
|
zephyr,flash = &flash0;
|
||||||
zmk,kscan = &kscan0;
|
zmk,kscan = &kscan0;
|
||||||
|
zmk,matrix_transform = &layout_grid_transform;
|
||||||
};
|
};
|
||||||
|
|
||||||
kscan0: kscan {
|
kscan0: kscan {
|
||||||
compatible = "zmk,kscan-gpio-matrix";
|
compatible = "zmk,kscan-gpio-matrix";
|
||||||
label = "KSCAN";
|
label = "KSCAN";
|
||||||
|
diode-direction = "col2row";
|
||||||
row-gpios
|
row-gpios
|
||||||
= <&gpioa 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
= <&gpioa 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||||
, <&gpioa 9 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
, <&gpioa 9 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||||
|
@ -40,6 +43,42 @@
|
||||||
;
|
;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
layout_grid_transform:
|
||||||
|
keymap_transform_0 {
|
||||||
|
compatible = "zmk,matrix-transform";
|
||||||
|
columns = <6>;
|
||||||
|
rows = <8>;
|
||||||
|
map = <
|
||||||
|
RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(4,0) RC(4,1) RC(4,2) RC(4,3) RC(4,4) RC(4,5)
|
||||||
|
RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(5,0) RC(5,1) RC(5,2) RC(5,3) RC(5,4) RC(5,5)
|
||||||
|
RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(6,0) RC(6,1) RC(6,2) RC(6,3) RC(6,4) RC(6,5)
|
||||||
|
RC(3,0) RC(3,1) RC(3,2) RC(7,3) RC(7,4) RC(7,5) RC(7,0) RC(7,1) RC(7,2) RC(3,3) RC(3,4) RC(3,5)
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
layout_mit_transform:
|
||||||
|
keymap_transform_1 {
|
||||||
|
compatible = "zmk,matrix-transform";
|
||||||
|
columns = <6>;
|
||||||
|
rows = <8>;
|
||||||
|
map = <
|
||||||
|
RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(4,0) RC(4,1) RC(4,2) RC(4,3) RC(4,4) RC(4,5)
|
||||||
|
RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(5,0) RC(5,1) RC(5,2) RC(5,3) RC(5,4) RC(5,5)
|
||||||
|
RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(6,0) RC(6,1) RC(6,2) RC(6,3) RC(6,4) RC(6,5)
|
||||||
|
RC(3,0) RC(3,1) RC(3,2) RC(7,3) RC(7,4) RC(7,0) RC(7,1) RC(7,2) RC(3,3) RC(3,4) RC(3,5)
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
layout_2x2u_transform:
|
||||||
|
keymap_transform_2 {
|
||||||
|
compatible = "zmk,matrix-transform";
|
||||||
|
columns = <6>;
|
||||||
|
rows = <8>;
|
||||||
|
map = <
|
||||||
|
RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(4,0) RC(4,1) RC(4,2) RC(4,3) RC(4,4) RC(4,5)
|
||||||
|
RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(5,0) RC(5,1) RC(5,2) RC(5,3) RC(5,4) RC(5,5)
|
||||||
|
RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(6,0) RC(6,1) RC(6,2) RC(6,3) RC(6,4) RC(6,5)
|
||||||
|
RC(3,0) RC(3,1) RC(3,2) RC(7,3) RC(7,5) RC(7,1) RC(7,2) RC(3,3) RC(3,4) RC(3,5)
|
||||||
|
>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
&usb {
|
&usb {
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
if(CONFIG_PINMUX)
|
|
||||||
zephyr_library()
|
|
||||||
zephyr_library_sources(pinmux.c)
|
|
||||||
zephyr_library_include_directories(${ZEPHYR_BASE}/drivers)
|
|
||||||
endif()
|
|
|
@ -48,5 +48,5 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
pro_micro_i2c: &i2c1 {};
|
pro_micro_i2c: &i2c1 {};
|
||||||
pro_micro_spi: &spi1 {};
|
pro_micro_spi: &spi2 {};
|
||||||
pro_micro_serial: &usart1 {};
|
pro_micro_serial: &usart1 {};
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2017 I-SENSE group of ICCS
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <kernel.h>
|
|
||||||
#include <device.h>
|
|
||||||
#include <init.h>
|
|
||||||
#include <drivers/pinmux.h>
|
|
||||||
#include <sys/sys_io.h>
|
|
||||||
|
|
||||||
#include <pinmux/stm32/pinmux_stm32.h>
|
|
||||||
|
|
||||||
/* pin assignments for STM32F3DISCOVERY board */
|
|
||||||
static const struct pin_config pinconf[] = {
|
|
||||||
#if DT_NODE_HAS_STATUS(DT_NODELABEL(usart1), okay) && CONFIG_SERIAL
|
|
||||||
{STM32_PIN_PC4, STM32F3_PINMUX_FUNC_PC4_USART1_TX},
|
|
||||||
{STM32_PIN_PC5, STM32F3_PINMUX_FUNC_PC5_USART1_RX},
|
|
||||||
#endif
|
|
||||||
#if DT_NODE_HAS_STATUS(DT_NODELABEL(usart2), okay) && CONFIG_SERIAL
|
|
||||||
{STM32_PIN_PA2, STM32F3_PINMUX_FUNC_PA2_USART2_TX},
|
|
||||||
{STM32_PIN_PA3, STM32F3_PINMUX_FUNC_PA3_USART2_RX},
|
|
||||||
#endif
|
|
||||||
#if DT_NODE_HAS_STATUS(DT_NODELABEL(i2c1), okay) && CONFIG_I2C
|
|
||||||
{STM32_PIN_PB6, STM32F3_PINMUX_FUNC_PB6_I2C1_SCL},
|
|
||||||
{STM32_PIN_PB7, STM32F3_PINMUX_FUNC_PB7_I2C1_SDA},
|
|
||||||
#endif
|
|
||||||
#if DT_NODE_HAS_STATUS(DT_NODELABEL(i2c2), okay) && CONFIG_I2C
|
|
||||||
{STM32_PIN_PA9, STM32F3_PINMUX_FUNC_PA9_I2C2_SCL},
|
|
||||||
{STM32_PIN_PA10, STM32F3_PINMUX_FUNC_PA10_I2C2_SDA},
|
|
||||||
#endif
|
|
||||||
#if DT_NODE_HAS_STATUS(DT_NODELABEL(spi1), okay) && CONFIG_SPI
|
|
||||||
#ifdef CONFIG_SPI_STM32_USE_HW_SS
|
|
||||||
{STM32_PIN_PA4, STM32F3_PINMUX_FUNC_PA4_SPI1_NSS},
|
|
||||||
#endif /* CONFIG_SPI_STM32_USE_HW_SS */
|
|
||||||
{STM32_PIN_PA5, STM32F3_PINMUX_FUNC_PA5_SPI1_SCK},
|
|
||||||
{STM32_PIN_PA6, STM32F3_PINMUX_FUNC_PA6_SPI1_MISO},
|
|
||||||
{STM32_PIN_PA7, STM32F3_PINMUX_FUNC_PA7_SPI1_MOSI},
|
|
||||||
#endif
|
|
||||||
#if DT_NODE_HAS_STATUS(DT_NODELABEL(spi2), okay) && CONFIG_SPI
|
|
||||||
#ifdef CONFIG_SPI_STM32_USE_HW_SS
|
|
||||||
{STM32_PIN_PB12, STM32F3_PINMUX_FUNC_PB12_SPI2_NSS},
|
|
||||||
#endif /* CONFIG_SPI_STM32_USE_HW_SS */
|
|
||||||
{STM32_PIN_PB13, STM32F3_PINMUX_FUNC_PB13_SPI2_SCK},
|
|
||||||
{STM32_PIN_PB14, STM32F3_PINMUX_FUNC_PB14_SPI2_MISO},
|
|
||||||
{STM32_PIN_PB15, STM32F3_PINMUX_FUNC_PB15_SPI2_MOSI},
|
|
||||||
#endif
|
|
||||||
#ifdef CONFIG_USB_DC_STM32
|
|
||||||
{STM32_PIN_PA11, STM32F3_PINMUX_FUNC_PA11_USB_DM},
|
|
||||||
{STM32_PIN_PA12, STM32F3_PINMUX_FUNC_PA12_USB_DP},
|
|
||||||
#endif /* CONFIG_USB_DC_STM32 */
|
|
||||||
#if DT_NODE_HAS_STATUS(DT_NODELABEL(can1), okay) && CONFIG_CAN
|
|
||||||
{STM32_PIN_PD0, STM32F3_PINMUX_FUNC_PD0_CAN1_RX},
|
|
||||||
{STM32_PIN_PD1, STM32F3_PINMUX_FUNC_PD1_CAN1_TX},
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
static int pinmux_stm32_init(const struct device *port) {
|
|
||||||
ARG_UNUSED(port);
|
|
||||||
|
|
||||||
stm32_setup_pins(pinconf, ARRAY_SIZE(pinconf));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SYS_INIT(pinmux_stm32_init, PRE_KERNEL_1, CONFIG_PINMUX_STM32_DEVICE_INITIALIZATION_PRIORITY);
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
/dts-v1/;
|
/dts-v1/;
|
||||||
#include <st/f3/stm32f303Xc.dtsi>
|
#include <st/f3/stm32f303Xc.dtsi>
|
||||||
|
#include <st/f3/stm32f303c(b-c)tx-pinctrl.dtsi>
|
||||||
#include "arduino_pro_micro_pins.dtsi"
|
#include "arduino_pro_micro_pins.dtsi"
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
|
@ -26,6 +27,18 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
&usart1 {
|
||||||
|
pinctrl-0 = <&usart1_tx_pa9 &usart1_rx_pa10>;
|
||||||
|
};
|
||||||
|
|
||||||
|
&spi2 {
|
||||||
|
pinctrl-0 = <&spi2_sck_pb13 &spi2_miso_pb14 &spi2_mosi_pb15>;
|
||||||
|
};
|
||||||
|
|
||||||
|
&i2c1 {
|
||||||
|
pinctrl-0 = <&i2c1_scl_pb6 &i2c1_sda_pb7>;
|
||||||
|
};
|
||||||
|
|
||||||
&usb {
|
&usb {
|
||||||
status = "okay";
|
status = "okay";
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,40 +16,40 @@
|
||||||
// -----------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------
|
||||||
// | TAB | Q | W | E | R | T | | Y | U | I | O | P | BKSP |
|
// | TAB | Q | W | E | R | T | | Y | U | I | O | P | BKSP |
|
||||||
// | CTRL | A | S | D | F | G | | H | J | K | L | ; | ' |
|
// | CTRL | A | S | D | F | G | | H | J | K | L | ; | ' |
|
||||||
// | SHFT | Z | X | C | V | B | | N | M | , | . | / | SHFT |
|
// | SHFT | Z | X | C | V | B | | N | M | , | . | / | ESC |
|
||||||
// | GUI | LWR | SPC | | ENT | RSE | ALT |
|
// | GUI | LWR | SPC | | ENT | RSE | ALT |
|
||||||
bindings = <
|
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 TAB &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp BSPC
|
||||||
&kp LCTRL &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp SQT
|
&kp LCTRL &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 FSLH &kp RSHFT
|
&kp LSHFT &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp ESC
|
||||||
&kp LGUI &mo 1 &kp SPACE &kp RET &mo 2 &kp RALT
|
&kp LGUI &mo 1 &kp SPACE &kp RET &mo 2 &kp RALT
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
lower_layer {
|
lower_layer {
|
||||||
// -----------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------
|
||||||
// | ESC | 1 | 2 | 3 | 4 | 5 | | 6 | 7 | 8 | 9 | 0 | BKSP |
|
// | TAB | 1 | 2 | 3 | 4 | 5 | | 6 | 7 | 8 | 9 | 0 | BKSP |
|
||||||
// | BTCLR| BT1 | BT2 | BT3 | BT4 | BT5 | | LFT | DWN | UP | RGT | | |
|
// | BTCLR| BT1 | BT2 | BT3 | BT4 | BT5 | | LFT | DWN | UP | RGT | | |
|
||||||
// | SHFT | | | | | | | | | | | | |
|
// | SHFT | | | | | | | | | | | | |
|
||||||
// | GUI | | SPC | | ENT | | ALT |
|
// | GUI | | SPC | | ENT | | ALT |
|
||||||
bindings = <
|
bindings = <
|
||||||
&kp ESC &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp BSPC
|
&kp TAB &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp BSPC
|
||||||
&bt BT_CLR &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 &kp LEFT &kp DOWN &kp UP &kp RIGHT &trans &trans
|
&bt BT_CLR &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 &kp LEFT &kp DOWN &kp UP &kp RIGHT &trans &trans
|
||||||
&kp LSHFT &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
|
&kp LSHFT &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
|
||||||
&kp LGUI &trans &kp SPACE &kp RET &trans &kp RALT
|
&kp LGUI &trans &kp SPACE &kp RET &trans &kp RALT
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
|
|
||||||
raise_layer {
|
raise_layer {
|
||||||
// -----------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------
|
||||||
// | ESC | ! | @ | # | $ | % | | ^ | & | * | ( | ) | BKSP |
|
// | TAB | ! | @ | # | $ | % | | ^ | & | * | ( | ) | BKSP |
|
||||||
// | CTRL | | | | | | | - | = | { | } | "|" | ` |
|
// | CTRL | | | | | | | - | = | [ | ] | \ | ` |
|
||||||
// | SHFT | | | | | | | _ | + | [ | ] | \ | ~ | // TODO: Fix this row when &mkp is committed
|
// | SHFT | | | | | | | _ | + | { | } | "|" | ~ |
|
||||||
// | GUI | | SPC | | ENT | | ALT |
|
// | GUI | | SPC | | ENT | | ALT |
|
||||||
bindings = <
|
bindings = <
|
||||||
&kp ESC &kp EXCL &kp AT &kp HASH &kp DLLR &kp PRCNT &kp CARET &kp AMPS &kp KP_MULTIPLY &kp LPAR &kp RPAR &kp BSPC
|
&kp TAB &kp EXCL &kp AT &kp HASH &kp DLLR &kp PRCNT &kp CARET &kp AMPS &kp KP_MULTIPLY &kp LPAR &kp RPAR &kp BSPC
|
||||||
&kp LCTRL &trans &trans &trans &trans &trans &kp MINUS &kp EQUAL &kp LBKT &kp RBKT &kp PIPE &kp GRAVE
|
&kp LCTRL &trans &trans &trans &trans &trans &kp MINUS &kp EQUAL &kp LBKT &kp RBKT &kp BSLH &kp GRAVE
|
||||||
&kp LSHFT &trans &trans &trans &trans &trans &trans &trans &trans &trans &kp BSLH &kp TILDE
|
&kp LSHFT &trans &trans &trans &trans &trans &kp UNDER &kp PLUS &kp LBRC &kp RBRC &kp PIPE &kp TILDE
|
||||||
&kp LGUI &trans &kp SPACE &kp RET &trans &kp RALT
|
&kp LGUI &trans &kp SPACE &kp RET &trans &kp RALT
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1 +1,35 @@
|
||||||
|
# Cradio
|
||||||
|
|
||||||
Cradio is a firmware for a few 34 key keyboards, including Cradio, Hypergolic and Sweep.
|
Cradio is a firmware for a few 34 key keyboards, including Cradio, Hypergolic and Sweep.
|
||||||
|
|
||||||
|
## Pin arrangement
|
||||||
|
|
||||||
|
Some revisions of the aforementioned PCBs have slightly different pin arrangements compared to what's defined in [`cradio.dtsi`](./cradio.dtsi). If you need to swap a few keys for your particular PCB, you can easily reorder the `input-gpio` definition in your own keymap file (i.e. in `zmk-config/config/cradio.keymap`):
|
||||||
|
|
||||||
|
```dts
|
||||||
|
/* Adjusted Cradio pin arrangement */
|
||||||
|
/* The position of Q and B keys have been swapped */
|
||||||
|
&kscan0 {
|
||||||
|
input-gpios
|
||||||
|
= <&pro_micro_d 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||||
|
, <&pro_micro_a 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||||
|
, <&pro_micro_a 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||||
|
, <&pro_micro_a 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||||
|
, <&pro_micro_a 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||||
|
, <&pro_micro_d 15 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||||
|
, <&pro_micro_d 14 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||||
|
, <&pro_micro_d 16 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||||
|
, <&pro_micro_d 10 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||||
|
, <&pro_micro_d 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||||
|
, <&pro_micro_d 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||||
|
, <&pro_micro_d 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||||
|
, <&pro_micro_d 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||||
|
, <&pro_micro_d 5 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||||
|
, <&pro_micro_d 7 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||||
|
, <&pro_micro_d 8 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||||
|
, <&pro_micro_d 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||||
|
;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
This `&kscan0` block must be placed outside of any blocks surrounded by curly braces (`{...}`).
|
||||||
|
|
|
@ -2,4 +2,5 @@
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
add_subdirectory(kscan)
|
add_subdirectory(kscan)
|
||||||
add_subdirectory(sensor)
|
add_subdirectory(sensor)
|
||||||
|
add_subdirectory(display)
|
|
@ -2,4 +2,5 @@
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
rsource "kscan/Kconfig"
|
rsource "kscan/Kconfig"
|
||||||
rsource "sensor/Kconfig"
|
rsource "sensor/Kconfig"
|
||||||
|
rsource "display/Kconfig"
|
4
app/drivers/display/CMakeLists.txt
Normal file
4
app/drivers/display/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Copyright (c) 2021 The ZMK Contributors
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
zephyr_sources_ifdef(CONFIG_IL0323 il0323.c)
|
4
app/drivers/display/Kconfig
Normal file
4
app/drivers/display/Kconfig
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Copyright (c) 2021 The ZMK Contributors
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
rsource "Kconfig.il0323"
|
11
app/drivers/display/Kconfig.il0323
Normal file
11
app/drivers/display/Kconfig.il0323
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Copyright (c) 2020 Phytec Messtechnik GmbH, Peter Johanson
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
# IL0323 display controller configuration options
|
||||||
|
|
||||||
|
config IL0323
|
||||||
|
bool "IL0323 compatible display controller driver"
|
||||||
|
depends on SPI
|
||||||
|
depends on HEAP_MEM_POOL_SIZE != 0
|
||||||
|
help
|
||||||
|
Enable driver for IL0323 compatible controller.
|
432
app/drivers/display/il0323.c
Normal file
432
app/drivers/display/il0323.c
Normal file
|
@ -0,0 +1,432 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 PHYTEC Messtechnik GmbHH, Peter Johanson
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT gooddisplay_il0323
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <device.h>
|
||||||
|
#include <init.h>
|
||||||
|
#include <drivers/display.h>
|
||||||
|
#include <drivers/gpio.h>
|
||||||
|
#include <drivers/spi.h>
|
||||||
|
#include <sys/byteorder.h>
|
||||||
|
|
||||||
|
#include "il0323_regs.h"
|
||||||
|
|
||||||
|
#include <logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(il0323, CONFIG_DISPLAY_LOG_LEVEL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IL0323 compatible EPD controller driver.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define IL0323_SPI_FREQ DT_INST_PROP(0, spi_max_frequency)
|
||||||
|
#define IL0323_BUS_NAME DT_INST_BUS_LABEL(0)
|
||||||
|
#define IL0323_DC_PIN DT_INST_GPIO_PIN(0, dc_gpios)
|
||||||
|
#define IL0323_DC_FLAGS DT_INST_GPIO_FLAGS(0, dc_gpios)
|
||||||
|
#define IL0323_DC_CNTRL DT_INST_GPIO_LABEL(0, dc_gpios)
|
||||||
|
#define IL0323_CS_PIN DT_INST_SPI_DEV_CS_GPIOS_PIN(0)
|
||||||
|
#define IL0323_CS_FLAGS DT_INST_SPI_DEV_CS_GPIOS_FLAGS(0)
|
||||||
|
#if DT_INST_SPI_DEV_HAS_CS_GPIOS(0)
|
||||||
|
#define IL0323_CS_CNTRL DT_INST_SPI_DEV_CS_GPIOS_LABEL(0)
|
||||||
|
#endif
|
||||||
|
#define IL0323_BUSY_PIN DT_INST_GPIO_PIN(0, busy_gpios)
|
||||||
|
#define IL0323_BUSY_CNTRL DT_INST_GPIO_LABEL(0, busy_gpios)
|
||||||
|
#define IL0323_BUSY_FLAGS DT_INST_GPIO_FLAGS(0, busy_gpios)
|
||||||
|
#define IL0323_RESET_PIN DT_INST_GPIO_PIN(0, reset_gpios)
|
||||||
|
#define IL0323_RESET_CNTRL DT_INST_GPIO_LABEL(0, reset_gpios)
|
||||||
|
#define IL0323_RESET_FLAGS DT_INST_GPIO_FLAGS(0, reset_gpios)
|
||||||
|
|
||||||
|
#define EPD_PANEL_WIDTH DT_INST_PROP(0, width)
|
||||||
|
#define EPD_PANEL_HEIGHT DT_INST_PROP(0, height)
|
||||||
|
#define IL0323_PIXELS_PER_BYTE 8U
|
||||||
|
|
||||||
|
/* Horizontally aligned page! */
|
||||||
|
#define IL0323_NUMOF_PAGES (EPD_PANEL_WIDTH / IL0323_PIXELS_PER_BYTE)
|
||||||
|
#define IL0323_PANEL_FIRST_GATE 0U
|
||||||
|
#define IL0323_PANEL_LAST_GATE (EPD_PANEL_HEIGHT - 1)
|
||||||
|
#define IL0323_PANEL_FIRST_PAGE 0U
|
||||||
|
#define IL0323_PANEL_LAST_PAGE (IL0323_NUMOF_PAGES - 1)
|
||||||
|
#define IL0323_BUFFER_SIZE 1280
|
||||||
|
|
||||||
|
struct il0323_data {
|
||||||
|
const struct device *reset;
|
||||||
|
const struct device *dc;
|
||||||
|
const struct device *busy;
|
||||||
|
const struct device *spi_dev;
|
||||||
|
struct spi_config spi_config;
|
||||||
|
#if defined(IL0323_CS_CNTRL)
|
||||||
|
struct spi_cs_control cs_ctrl;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint8_t il0323_pwr[] = DT_INST_PROP(0, pwr);
|
||||||
|
|
||||||
|
static uint8_t last_buffer[IL0323_BUFFER_SIZE];
|
||||||
|
static bool blanking_on = true;
|
||||||
|
|
||||||
|
static inline int il0323_write_cmd(struct il0323_data *driver, uint8_t cmd, uint8_t *data,
|
||||||
|
size_t len) {
|
||||||
|
struct spi_buf buf = {.buf = &cmd, .len = sizeof(cmd)};
|
||||||
|
struct spi_buf_set buf_set = {.buffers = &buf, .count = 1};
|
||||||
|
|
||||||
|
gpio_pin_set(driver->dc, IL0323_DC_PIN, 1);
|
||||||
|
if (spi_write(driver->spi_dev, &driver->spi_config, &buf_set)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data != NULL) {
|
||||||
|
buf.buf = data;
|
||||||
|
buf.len = len;
|
||||||
|
gpio_pin_set(driver->dc, IL0323_DC_PIN, 0);
|
||||||
|
if (spi_write(driver->spi_dev, &driver->spi_config, &buf_set)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void il0323_busy_wait(struct il0323_data *driver) {
|
||||||
|
int pin = gpio_pin_get(driver->busy, IL0323_BUSY_PIN);
|
||||||
|
|
||||||
|
while (pin > 0) {
|
||||||
|
__ASSERT(pin >= 0, "Failed to get pin level");
|
||||||
|
// LOG_DBG("wait %u", pin);
|
||||||
|
k_msleep(IL0323_BUSY_DELAY);
|
||||||
|
pin = gpio_pin_get(driver->busy, IL0323_BUSY_PIN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int il0323_update_display(const struct device *dev) {
|
||||||
|
struct il0323_data *driver = dev->data;
|
||||||
|
|
||||||
|
LOG_DBG("Trigger update sequence");
|
||||||
|
if (il0323_write_cmd(driver, IL0323_CMD_DRF, NULL, 0)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_msleep(IL0323_BUSY_DELAY);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int il0323_blanking_off(const struct device *dev) {
|
||||||
|
struct il0323_data *driver = dev->data;
|
||||||
|
|
||||||
|
if (blanking_on) {
|
||||||
|
/* Update EPD pannel in normal mode */
|
||||||
|
il0323_busy_wait(driver);
|
||||||
|
if (il0323_update_display(dev)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blanking_on = false;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int il0323_blanking_on(const struct device *dev) {
|
||||||
|
blanking_on = true;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int il0323_write(const struct device *dev, const uint16_t x, const uint16_t y,
|
||||||
|
const struct display_buffer_descriptor *desc, const void *buf) {
|
||||||
|
struct il0323_data *driver = dev->data;
|
||||||
|
uint16_t x_end_idx = x + desc->width - 1;
|
||||||
|
uint16_t y_end_idx = y + desc->height - 1;
|
||||||
|
uint8_t ptl[IL0323_PTL_REG_LENGTH] = {0};
|
||||||
|
size_t buf_len;
|
||||||
|
|
||||||
|
LOG_DBG("x %u, y %u, height %u, width %u, pitch %u", x, y, desc->height, desc->width,
|
||||||
|
desc->pitch);
|
||||||
|
|
||||||
|
buf_len = MIN(desc->buf_size, desc->height * desc->width / IL0323_PIXELS_PER_BYTE);
|
||||||
|
__ASSERT(desc->width <= desc->pitch, "Pitch is smaller then width");
|
||||||
|
__ASSERT(buf != NULL, "Buffer is not available");
|
||||||
|
__ASSERT(buf_len != 0U, "Buffer of length zero");
|
||||||
|
__ASSERT(!(desc->width % IL0323_PIXELS_PER_BYTE), "Buffer width not multiple of %d",
|
||||||
|
IL0323_PIXELS_PER_BYTE);
|
||||||
|
|
||||||
|
LOG_DBG("buf_len %d", buf_len);
|
||||||
|
if ((y_end_idx > (EPD_PANEL_HEIGHT - 1)) || (x_end_idx > (EPD_PANEL_WIDTH - 1))) {
|
||||||
|
LOG_ERR("Position out of bounds");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup Partial Window and enable Partial Mode */
|
||||||
|
ptl[IL0323_PTL_HRST_IDX] = x;
|
||||||
|
ptl[IL0323_PTL_HRED_IDX] = x_end_idx;
|
||||||
|
ptl[IL0323_PTL_VRST_IDX] = y;
|
||||||
|
ptl[IL0323_PTL_VRED_IDX] = y_end_idx;
|
||||||
|
ptl[sizeof(ptl) - 1] = IL0323_PTL_PT_SCAN;
|
||||||
|
LOG_HEXDUMP_DBG(ptl, sizeof(ptl), "ptl");
|
||||||
|
|
||||||
|
il0323_busy_wait(driver);
|
||||||
|
if (il0323_write_cmd(driver, IL0323_CMD_PIN, NULL, 0)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (il0323_write_cmd(driver, IL0323_CMD_PTL, ptl, sizeof(ptl))) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (il0323_write_cmd(driver, IL0323_CMD_DTM1, last_buffer, IL0323_BUFFER_SIZE)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (il0323_write_cmd(driver, IL0323_CMD_DTM2, (uint8_t *)buf, buf_len)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(last_buffer, (uint8_t *)buf, IL0323_BUFFER_SIZE);
|
||||||
|
|
||||||
|
/* Update partial window and disable Partial Mode */
|
||||||
|
if (blanking_on == false) {
|
||||||
|
if (il0323_update_display(dev)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (il0323_write_cmd(driver, IL0323_CMD_POUT, NULL, 0)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int il0323_read(const struct device *dev, const uint16_t x, const uint16_t y,
|
||||||
|
const struct display_buffer_descriptor *desc, void *buf) {
|
||||||
|
LOG_ERR("not supported");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *il0323_get_framebuffer(const struct device *dev) {
|
||||||
|
LOG_ERR("not supported");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int il0323_set_brightness(const struct device *dev, const uint8_t brightness) {
|
||||||
|
LOG_WRN("not supported");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int il0323_set_contrast(const struct device *dev, uint8_t contrast) {
|
||||||
|
LOG_WRN("not supported");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void il0323_get_capabilities(const struct device *dev, struct display_capabilities *caps) {
|
||||||
|
memset(caps, 0, sizeof(struct display_capabilities));
|
||||||
|
caps->x_resolution = EPD_PANEL_WIDTH;
|
||||||
|
caps->y_resolution = EPD_PANEL_HEIGHT;
|
||||||
|
caps->supported_pixel_formats = PIXEL_FORMAT_MONO10;
|
||||||
|
caps->current_pixel_format = PIXEL_FORMAT_MONO10;
|
||||||
|
caps->screen_info = SCREEN_INFO_MONO_MSB_FIRST | SCREEN_INFO_EPD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int il0323_set_orientation(const struct device *dev,
|
||||||
|
const enum display_orientation orientation) {
|
||||||
|
LOG_ERR("Unsupported");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int il0323_set_pixel_format(const struct device *dev, const enum display_pixel_format pf) {
|
||||||
|
if (pf == PIXEL_FORMAT_MONO10) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_ERR("not supported");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int il0323_clear_and_write_buffer(const struct device *dev, uint8_t pattern, bool update) {
|
||||||
|
struct display_buffer_descriptor desc = {
|
||||||
|
.buf_size = IL0323_NUMOF_PAGES,
|
||||||
|
.width = EPD_PANEL_WIDTH,
|
||||||
|
.height = 1,
|
||||||
|
.pitch = EPD_PANEL_WIDTH,
|
||||||
|
};
|
||||||
|
uint8_t *line;
|
||||||
|
|
||||||
|
line = k_malloc(IL0323_NUMOF_PAGES);
|
||||||
|
if (line == NULL) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(line, pattern, IL0323_NUMOF_PAGES);
|
||||||
|
for (int i = 0; i < EPD_PANEL_HEIGHT; i++) {
|
||||||
|
il0323_write(dev, 0, i, &desc, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
k_free(line);
|
||||||
|
|
||||||
|
if (update == true) {
|
||||||
|
if (il0323_update_display(dev)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int il0323_controller_init(const struct device *dev) {
|
||||||
|
struct il0323_data *driver = dev->data;
|
||||||
|
uint8_t tmp[IL0323_TRES_REG_LENGTH];
|
||||||
|
|
||||||
|
LOG_DBG("");
|
||||||
|
|
||||||
|
gpio_pin_set(driver->reset, IL0323_RESET_PIN, 1);
|
||||||
|
k_msleep(IL0323_RESET_DELAY);
|
||||||
|
gpio_pin_set(driver->reset, IL0323_RESET_PIN, 0);
|
||||||
|
k_msleep(IL0323_RESET_DELAY);
|
||||||
|
il0323_busy_wait(driver);
|
||||||
|
|
||||||
|
LOG_DBG("Initialize IL0323 controller");
|
||||||
|
|
||||||
|
if (il0323_write_cmd(driver, IL0323_CMD_PWR, il0323_pwr, sizeof(il0323_pwr))) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Turn on: booster, controller, regulators, and sensor. */
|
||||||
|
if (il0323_write_cmd(driver, IL0323_CMD_PON, NULL, 0)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_msleep(IL0323_PON_DELAY);
|
||||||
|
il0323_busy_wait(driver);
|
||||||
|
|
||||||
|
/* Pannel settings, KW mode */
|
||||||
|
tmp[0] = IL0323_PSR_UD | IL0323_PSR_SHL | IL0323_PSR_SHD | IL0323_PSR_RST;
|
||||||
|
#if EPD_PANEL_WIDTH == 80
|
||||||
|
|
||||||
|
#if EPD_PANEL_HEIGHT == 128
|
||||||
|
tmp[0] |= IL0323_PSR_RES_HEIGHT;
|
||||||
|
#endif /* panel height */
|
||||||
|
|
||||||
|
#else
|
||||||
|
tmp[0] |= IL0323_PSR_RES_WIDTH;
|
||||||
|
#if EPD_PANEL_HEIGHT == 96
|
||||||
|
tmp[0] |= IL0323_PSR_RES_HEIGHT;
|
||||||
|
#else
|
||||||
|
#endif /* panel height */
|
||||||
|
|
||||||
|
#endif /* panel width */
|
||||||
|
|
||||||
|
LOG_HEXDUMP_DBG(tmp, 1, "PSR");
|
||||||
|
if (il0323_write_cmd(driver, IL0323_CMD_PSR, tmp, 1)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set panel resolution */
|
||||||
|
tmp[IL0323_TRES_HRES_IDX] = EPD_PANEL_WIDTH;
|
||||||
|
tmp[IL0323_TRES_VRES_IDX] = EPD_PANEL_HEIGHT;
|
||||||
|
LOG_HEXDUMP_DBG(tmp, IL0323_TRES_REG_LENGTH, "TRES");
|
||||||
|
if (il0323_write_cmd(driver, IL0323_CMD_TRES, tmp, IL0323_TRES_REG_LENGTH)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp[IL0323_CDI_CDI_IDX] = DT_INST_PROP(0, cdi);
|
||||||
|
LOG_HEXDUMP_DBG(tmp, IL0323_CDI_REG_LENGTH, "CDI");
|
||||||
|
if (il0323_write_cmd(driver, IL0323_CMD_CDI, tmp, IL0323_CDI_REG_LENGTH)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp[0] = DT_INST_PROP(0, tcon);
|
||||||
|
if (il0323_write_cmd(driver, IL0323_CMD_TCON, tmp, 1)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable Auto Sequence */
|
||||||
|
tmp[0] = IL0323_AUTO_PON_DRF_POF;
|
||||||
|
if (il0323_write_cmd(driver, IL0323_CMD_AUTO, tmp, 1)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (il0323_clear_and_write_buffer(dev, 0xff, false)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int il0323_init(const struct device *dev) {
|
||||||
|
struct il0323_data *driver = dev->data;
|
||||||
|
|
||||||
|
LOG_DBG("");
|
||||||
|
|
||||||
|
driver->spi_dev = device_get_binding(IL0323_BUS_NAME);
|
||||||
|
if (driver->spi_dev == NULL) {
|
||||||
|
LOG_ERR("Could not get SPI device for IL0323");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
driver->spi_config.frequency = IL0323_SPI_FREQ;
|
||||||
|
driver->spi_config.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8);
|
||||||
|
driver->spi_config.slave = DT_INST_REG_ADDR(0);
|
||||||
|
driver->spi_config.cs = NULL;
|
||||||
|
|
||||||
|
driver->reset = device_get_binding(IL0323_RESET_CNTRL);
|
||||||
|
if (driver->reset == NULL) {
|
||||||
|
LOG_ERR("Could not get GPIO port for IL0323 reset");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_pin_configure(driver->reset, IL0323_RESET_PIN, GPIO_OUTPUT_INACTIVE | IL0323_RESET_FLAGS);
|
||||||
|
|
||||||
|
driver->dc = device_get_binding(IL0323_DC_CNTRL);
|
||||||
|
if (driver->dc == NULL) {
|
||||||
|
LOG_ERR("Could not get GPIO port for IL0323 DC signal");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_pin_configure(driver->dc, IL0323_DC_PIN, GPIO_OUTPUT_INACTIVE | IL0323_DC_FLAGS);
|
||||||
|
|
||||||
|
driver->busy = device_get_binding(IL0323_BUSY_CNTRL);
|
||||||
|
if (driver->busy == NULL) {
|
||||||
|
LOG_ERR("Could not get GPIO port for IL0323 busy signal");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_pin_configure(driver->busy, IL0323_BUSY_PIN, GPIO_INPUT | IL0323_BUSY_FLAGS);
|
||||||
|
|
||||||
|
#if defined(IL0323_CS_CNTRL)
|
||||||
|
driver->cs_ctrl.gpio_dev = device_get_binding(IL0323_CS_CNTRL);
|
||||||
|
if (!driver->cs_ctrl.gpio_dev) {
|
||||||
|
LOG_ERR("Unable to get SPI GPIO CS device");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
driver->cs_ctrl.gpio_pin = IL0323_CS_PIN;
|
||||||
|
driver->cs_ctrl.gpio_dt_flags = IL0323_CS_FLAGS;
|
||||||
|
driver->cs_ctrl.delay = 0U;
|
||||||
|
driver->spi_config.cs = &driver->cs_ctrl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return il0323_controller_init(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct il0323_data il0323_driver;
|
||||||
|
|
||||||
|
static struct display_driver_api il0323_driver_api = {
|
||||||
|
.blanking_on = il0323_blanking_on,
|
||||||
|
.blanking_off = il0323_blanking_off,
|
||||||
|
.write = il0323_write,
|
||||||
|
.read = il0323_read,
|
||||||
|
.get_framebuffer = il0323_get_framebuffer,
|
||||||
|
.set_brightness = il0323_set_brightness,
|
||||||
|
.set_contrast = il0323_set_contrast,
|
||||||
|
.get_capabilities = il0323_get_capabilities,
|
||||||
|
.set_pixel_format = il0323_set_pixel_format,
|
||||||
|
.set_orientation = il0323_set_orientation,
|
||||||
|
};
|
||||||
|
|
||||||
|
DEVICE_DT_INST_DEFINE(0, il0323_init, device_pm_control_nop, &il0323_driver, NULL, POST_KERNEL,
|
||||||
|
CONFIG_APPLICATION_INIT_PRIORITY, &il0323_driver_api);
|
81
app/drivers/display/il0323_regs.h
Normal file
81
app/drivers/display/il0323_regs.h
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 PHYTEC Messtechnik GmbH, Peter Johanson
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_DRIVERS_DISPLAY_IL0323_REGS_H_
|
||||||
|
#define ZEPHYR_DRIVERS_DISPLAY_IL0323_REGS_H_
|
||||||
|
|
||||||
|
#define IL0323_CMD_PSR 0x00
|
||||||
|
#define IL0323_CMD_PWR 0x01
|
||||||
|
#define IL0323_CMD_POF 0x02
|
||||||
|
#define IL0323_CMD_PFS 0x03
|
||||||
|
#define IL0323_CMD_PON 0x04
|
||||||
|
#define IL0323_CMD_PMES 0x05
|
||||||
|
#define IL0323_CMD_CPSET 0x06
|
||||||
|
#define IL0323_CMD_DSLP 0x07
|
||||||
|
#define IL0323_CMD_DTM1 0x10
|
||||||
|
#define IL0323_CMD_DSP 0x11
|
||||||
|
#define IL0323_CMD_DRF 0x12
|
||||||
|
#define IL0323_CMD_DTM2 0x13
|
||||||
|
#define IL0323_CMD_AUTO 0x17
|
||||||
|
#define IL0323_CMD_LUTOPT 0x2A
|
||||||
|
#define IL0323_CMD_PLL 0x30
|
||||||
|
#define IL0323_CMD_TSC 0x40
|
||||||
|
#define IL0323_CMD_TSE 0x41
|
||||||
|
#define IL0323_CMD_PBC 0x44
|
||||||
|
#define IL0323_CMD_CDI 0x50
|
||||||
|
#define IL0323_CMD_LPD 0x51
|
||||||
|
#define IL0323_CMD_TCON 0x60
|
||||||
|
#define IL0323_CMD_TRES 0x61
|
||||||
|
#define IL0323_CMD_GSST 0x65
|
||||||
|
#define IL0323_CMD_REV 0x70
|
||||||
|
#define IL0323_CMD_FLG 0x71
|
||||||
|
#define IL0323_CMD_CRC 0x72
|
||||||
|
#define IL0323_CMD_AMV 0x80
|
||||||
|
#define IL0323_CMD_VV 0x81
|
||||||
|
#define IL0323_CMD_VDCS 0x82
|
||||||
|
#define IL0323_CMD_PTL 0x90
|
||||||
|
#define IL0323_CMD_PIN 0x91
|
||||||
|
#define IL0323_CMD_POUT 0x92
|
||||||
|
#define IL0323_CMD_PGM 0xA0
|
||||||
|
#define IL0323_CMD_APG 0xA1
|
||||||
|
#define IL0323_CMD_ROTP 0xA2
|
||||||
|
#define IL0323_CMD_CCSET 0xE0
|
||||||
|
#define IL0323_CMD_PWS 0xE3
|
||||||
|
#define IL0323_CMD_LVSEL 0xE4
|
||||||
|
#define IL0323_CMD_TSSET 0xE5
|
||||||
|
|
||||||
|
#define IL0323_PSR_RES_WIDTH BIT(7)
|
||||||
|
#define IL0323_PSR_RES_HEIGHT BIT(6)
|
||||||
|
#define IL0323_PSR_LUT_REG BIT(5)
|
||||||
|
#define IL0323_PSR_LUT_OTP BIT(4)
|
||||||
|
#define IL0323_PSR_UD BIT(3)
|
||||||
|
#define IL0323_PSR_SHL BIT(2)
|
||||||
|
#define IL0323_PSR_SHD BIT(1)
|
||||||
|
#define IL0323_PSR_RST BIT(0)
|
||||||
|
|
||||||
|
#define IL0323_AUTO_PON_DRF_POF 0xA5
|
||||||
|
#define IL0323_AUTO_PON_DRF_POF_DSLP 0xA7
|
||||||
|
|
||||||
|
#define IL0323_CDI_REG_LENGTH 1U
|
||||||
|
#define IL0323_CDI_CDI_IDX 0
|
||||||
|
|
||||||
|
#define IL0323_TRES_REG_LENGTH 2U
|
||||||
|
#define IL0323_TRES_HRES_IDX 0
|
||||||
|
#define IL0323_TRES_VRES_IDX 1
|
||||||
|
|
||||||
|
#define IL0323_PTL_REG_LENGTH 5U
|
||||||
|
#define IL0323_PTL_HRST_IDX 0
|
||||||
|
#define IL0323_PTL_HRED_IDX 1
|
||||||
|
#define IL0323_PTL_VRST_IDX 2
|
||||||
|
#define IL0323_PTL_VRED_IDX 3
|
||||||
|
#define IL0323_PTL_PT_SCAN BIT(0)
|
||||||
|
|
||||||
|
/* Time constants in ms */
|
||||||
|
#define IL0323_RESET_DELAY 10U
|
||||||
|
#define IL0323_PON_DELAY 100U
|
||||||
|
#define IL0323_BUSY_DELAY 1U
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_DRIVERS_DISPLAY_IL0323_REGS_H_ */
|
|
@ -108,6 +108,6 @@ static const struct kscan_composite_config kscan_composite_config = {};
|
||||||
|
|
||||||
static struct kscan_composite_data kscan_composite_data;
|
static struct kscan_composite_data kscan_composite_data;
|
||||||
|
|
||||||
DEVICE_AND_API_INIT(kscan_composite, DT_INST_LABEL(0), kscan_composite_init, &kscan_composite_data,
|
DEVICE_DT_INST_DEFINE(0, kscan_composite_init, device_pm_control_nop, &kscan_composite_data,
|
||||||
&kscan_composite_config, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
|
&kscan_composite_config, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
|
||||||
&mock_driver_api);
|
&mock_driver_api);
|
||||||
|
|
|
@ -248,9 +248,9 @@ struct kscan_gpio_item_config {
|
||||||
.cols = {UTIL_LISTIFY(INST_DEMUX_GPIOS(n), _KSCAN_GPIO_OUTPUT_CFG_INIT, n)}, \
|
.cols = {UTIL_LISTIFY(INST_DEMUX_GPIOS(n), _KSCAN_GPIO_OUTPUT_CFG_INIT, n)}, \
|
||||||
}; \
|
}; \
|
||||||
\
|
\
|
||||||
DEVICE_AND_API_INIT(kscan_gpio_##n, DT_INST_LABEL(n), kscan_gpio_init_##n, \
|
DEVICE_DT_INST_DEFINE(n, kscan_gpio_init_##n, device_pm_control_nop, &kscan_gpio_data_##n, \
|
||||||
&kscan_gpio_data_##n, &kscan_gpio_config_##n, APPLICATION, \
|
&kscan_gpio_config_##n, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY, \
|
||||||
CONFIG_APPLICATION_INIT_PRIORITY, &gpio_driver_api_##n);
|
&gpio_driver_api_##n);
|
||||||
|
|
||||||
DT_INST_FOREACH_STATUS_OKAY(GPIO_INST_INIT)
|
DT_INST_FOREACH_STATUS_OKAY(GPIO_INST_INIT)
|
||||||
|
|
||||||
|
|
|
@ -238,9 +238,9 @@ static const struct kscan_driver_api gpio_driver_api = {
|
||||||
.inputs = {UTIL_LISTIFY(INST_INPUT_LEN(n), KSCAN_DIRECT_INPUT_ITEM, n)}, \
|
.inputs = {UTIL_LISTIFY(INST_INPUT_LEN(n), KSCAN_DIRECT_INPUT_ITEM, n)}, \
|
||||||
.num_of_inputs = INST_INPUT_LEN(n), \
|
.num_of_inputs = INST_INPUT_LEN(n), \
|
||||||
.debounce_period = DT_INST_PROP(n, debounce_period)}; \
|
.debounce_period = DT_INST_PROP(n, debounce_period)}; \
|
||||||
DEVICE_AND_API_INIT(kscan_gpio_##n, DT_INST_LABEL(n), kscan_gpio_init_##n, \
|
DEVICE_DT_INST_DEFINE(n, kscan_gpio_init_##n, device_pm_control_nop, &kscan_gpio_data_##n, \
|
||||||
&kscan_gpio_data_##n, &kscan_gpio_config_##n, POST_KERNEL, \
|
&kscan_gpio_config_##n, POST_KERNEL, CONFIG_ZMK_KSCAN_INIT_PRIORITY, \
|
||||||
CONFIG_ZMK_KSCAN_INIT_PRIORITY, &gpio_driver_api);
|
&gpio_driver_api);
|
||||||
|
|
||||||
DT_INST_FOREACH_STATUS_OKAY(GPIO_INST_INIT)
|
DT_INST_FOREACH_STATUS_OKAY(GPIO_INST_INIT)
|
||||||
|
|
||||||
|
|
|
@ -1,48 +1,158 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 The ZMK Contributors
|
* Copyright (c) 2020-2021 The ZMK Contributors
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define DT_DRV_COMPAT zmk_kscan_gpio_matrix
|
|
||||||
|
|
||||||
#include <device.h>
|
#include <device.h>
|
||||||
#include <drivers/kscan.h>
|
#include <devicetree.h>
|
||||||
#include <drivers/gpio.h>
|
#include <drivers/gpio.h>
|
||||||
|
#include <drivers/kscan.h>
|
||||||
#include <logging/log.h>
|
#include <logging/log.h>
|
||||||
|
#include <sys/__assert.h>
|
||||||
|
#include <sys/util.h>
|
||||||
|
|
||||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT zmk_kscan_gpio_matrix
|
||||||
|
|
||||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||||
|
|
||||||
struct kscan_gpio_item_config {
|
#define INST_DIODE_DIR(n) DT_ENUM_IDX(DT_DRV_INST(n), diode_direction)
|
||||||
char *label;
|
#define COND_DIODE_DIR(n, row2col_code, col2row_code) \
|
||||||
|
COND_CODE_0(INST_DIODE_DIR(n), row2col_code, col2row_code)
|
||||||
|
|
||||||
|
#define INST_ROWS_LEN(n) DT_INST_PROP_LEN(n, row_gpios)
|
||||||
|
#define INST_COLS_LEN(n) DT_INST_PROP_LEN(n, col_gpios)
|
||||||
|
#define INST_MATRIX_LEN(n) (INST_ROWS_LEN(n) * INST_COLS_LEN(n))
|
||||||
|
#define INST_INPUTS_LEN(n) COND_DIODE_DIR(n, (INST_COLS_LEN(n)), (INST_ROWS_LEN(n)))
|
||||||
|
|
||||||
|
#define USE_POLLING IS_ENABLED(CONFIG_ZMK_KSCAN_MATRIX_POLLING)
|
||||||
|
#define USE_INTERRUPTS (!USE_POLLING)
|
||||||
|
|
||||||
|
#define COND_INTERRUPTS(code) COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (), code)
|
||||||
|
#define COND_POLL_OR_INTERRUPTS(pollcode, intcode) \
|
||||||
|
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, pollcode, intcode)
|
||||||
|
|
||||||
|
// TODO (Zephr 2.6): replace the following
|
||||||
|
// kscan_gpio_dt_spec -> gpio_dt_spec
|
||||||
|
// KSCAN_GPIO_DT_SPEC_GET_BY_IDX -> GPIO_DT_SPEC_GET_BY_IDX
|
||||||
|
// gpio_pin_get -> gpio_pin_get_dt
|
||||||
|
// gpio_pin_set -> gpio_pin_set_dt
|
||||||
|
// gpio_pin_interrupt_configure -> gpio_pin_interrupt_configure_dt
|
||||||
|
struct kscan_gpio_dt_spec {
|
||||||
|
const struct device *port;
|
||||||
gpio_pin_t pin;
|
gpio_pin_t pin;
|
||||||
gpio_flags_t flags;
|
gpio_dt_flags_t dt_flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define _KSCAN_GPIO_ITEM_CFG_INIT(n, prop, idx) \
|
#define KSCAN_GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx) \
|
||||||
{ \
|
{ \
|
||||||
.label = DT_INST_GPIO_LABEL_BY_IDX(n, prop, idx), \
|
.port = DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(node_id, prop, idx)), \
|
||||||
.pin = DT_INST_GPIO_PIN_BY_IDX(n, prop, idx), \
|
.pin = DT_GPIO_PIN_BY_IDX(node_id, prop, idx), \
|
||||||
.flags = DT_INST_GPIO_FLAGS_BY_IDX(n, prop, idx), \
|
.dt_flags = DT_GPIO_FLAGS_BY_IDX(node_id, prop, idx), \
|
||||||
},
|
}
|
||||||
|
|
||||||
#define _KSCAN_GPIO_ROW_CFG_INIT(idx, n) _KSCAN_GPIO_ITEM_CFG_INIT(n, row_gpios, idx)
|
#define KSCAN_GPIO_ROW_CFG_INIT(idx, inst_idx) \
|
||||||
#define _KSCAN_GPIO_COL_CFG_INIT(idx, n) _KSCAN_GPIO_ITEM_CFG_INIT(n, col_gpios, idx)
|
KSCAN_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst_idx), row_gpios, idx),
|
||||||
|
#define KSCAN_GPIO_COL_CFG_INIT(idx, inst_idx) \
|
||||||
|
KSCAN_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst_idx), col_gpios, idx),
|
||||||
|
|
||||||
#if !defined(CONFIG_ZMK_KSCAN_MATRIX_POLLING)
|
enum kscan_diode_direction {
|
||||||
static int kscan_gpio_config_interrupts(const struct device **devices,
|
KSCAN_ROW2COL,
|
||||||
const struct kscan_gpio_item_config *configs, size_t len,
|
KSCAN_COL2ROW,
|
||||||
gpio_flags_t flags) {
|
};
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
const struct device *dev = devices[i];
|
|
||||||
const struct kscan_gpio_item_config *cfg = &configs[i];
|
|
||||||
|
|
||||||
int err = gpio_pin_interrupt_configure(dev, cfg->pin, flags);
|
struct kscan_matrix_irq_callback {
|
||||||
|
const struct device *dev;
|
||||||
|
struct gpio_callback callback;
|
||||||
|
struct k_delayed_work *work;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kscan_matrix_data {
|
||||||
|
const struct device *dev;
|
||||||
|
kscan_callback_t callback;
|
||||||
|
struct k_delayed_work work;
|
||||||
|
#if USE_POLLING
|
||||||
|
struct k_timer poll_timer;
|
||||||
|
#else
|
||||||
|
/** Array of length config->inputs.len */
|
||||||
|
struct kscan_matrix_irq_callback *irqs;
|
||||||
|
#endif
|
||||||
|
/**
|
||||||
|
* Current state of the matrix as a flattened 2D array of length
|
||||||
|
* (config->rows.len * config->cols.len)
|
||||||
|
*/
|
||||||
|
bool *current_state;
|
||||||
|
/** Buffer for reading in the next matrix state. Parallel array to current_state. */
|
||||||
|
bool *next_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kscan_gpio_list {
|
||||||
|
const struct kscan_gpio_dt_spec *gpios;
|
||||||
|
size_t len;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Define a kscan_gpio_list from a compile-time GPIO array. */
|
||||||
|
#define KSCAN_GPIO_LIST(gpio_array) \
|
||||||
|
((struct kscan_gpio_list){.gpios = gpio_array, .len = ARRAY_SIZE(gpio_array)})
|
||||||
|
|
||||||
|
struct kscan_matrix_config {
|
||||||
|
struct kscan_gpio_list rows;
|
||||||
|
struct kscan_gpio_list cols;
|
||||||
|
struct kscan_gpio_list inputs;
|
||||||
|
struct kscan_gpio_list outputs;
|
||||||
|
int32_t debounce_period_ms;
|
||||||
|
int32_t poll_period_ms;
|
||||||
|
enum kscan_diode_direction diode_direction;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the index into a matrix state array from a row and column.
|
||||||
|
*/
|
||||||
|
static int state_index_rc(const struct kscan_matrix_config *config, const int row, const int col) {
|
||||||
|
__ASSERT(row < config->rows.len, "Invalid row %i", row);
|
||||||
|
__ASSERT(col < config->cols.len, "Invalid column %i", col);
|
||||||
|
|
||||||
|
return (col * config->rows.len) + row;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the index into a matrix state array from input/output pin indices.
|
||||||
|
*/
|
||||||
|
static int state_index_io(const struct kscan_matrix_config *config, const int input_idx,
|
||||||
|
const int output_idx) {
|
||||||
|
return (config->diode_direction == KSCAN_ROW2COL)
|
||||||
|
? state_index_rc(config, output_idx, input_idx)
|
||||||
|
: state_index_rc(config, input_idx, output_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kscan_matrix_set_all_outputs(const struct device *dev, const int value) {
|
||||||
|
const struct kscan_matrix_config *config = dev->config;
|
||||||
|
|
||||||
|
for (int i = 0; i < config->outputs.len; i++) {
|
||||||
|
const struct kscan_gpio_dt_spec *gpio = &config->outputs.gpios[i];
|
||||||
|
|
||||||
|
int err = gpio_pin_set(gpio->port, gpio->pin, value);
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_ERR("Unable to enable matrix GPIO interrupt");
|
LOG_ERR("Failed to set output %i to %i: %i", i, value, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_INTERRUPTS
|
||||||
|
static int kscan_matrix_interrupt_configure(const struct device *dev, const gpio_flags_t flags) {
|
||||||
|
const struct kscan_matrix_config *config = dev->config;
|
||||||
|
|
||||||
|
for (int i = 0; i < config->inputs.len; i++) {
|
||||||
|
const struct kscan_gpio_dt_spec *gpio = &config->inputs.gpios[i];
|
||||||
|
|
||||||
|
int err = gpio_pin_interrupt_configure(gpio->port, gpio->pin, flags);
|
||||||
|
if (err) {
|
||||||
|
LOG_ERR("Unable to configure interrupt for pin %u on %s", gpio->pin, gpio->port->name);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,257 +161,299 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define COND_POLLING(code) COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (code), ())
|
#if USE_INTERRUPTS
|
||||||
#define COND_INTERRUPTS(code) COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (), (code))
|
static int kscan_matrix_interrupt_enable(const struct device *dev) {
|
||||||
#define COND_POLL_OR_INTERRUPTS(pollcode, intcode) \
|
int err = kscan_matrix_interrupt_configure(dev, GPIO_INT_LEVEL_ACTIVE);
|
||||||
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, pollcode, intcode)
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
#define INST_MATRIX_ROWS(n) DT_INST_PROP_LEN(n, row_gpios)
|
// While interrupts are enabled, set all outputs active so a pressed key
|
||||||
#define INST_MATRIX_COLS(n) DT_INST_PROP_LEN(n, col_gpios)
|
// will trigger an interrupt.
|
||||||
#define INST_OUTPUT_LEN(n) \
|
return kscan_matrix_set_all_outputs(dev, 1);
|
||||||
COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (INST_MATRIX_ROWS(n)), \
|
}
|
||||||
(INST_MATRIX_COLS(n)))
|
#endif
|
||||||
#define INST_INPUT_LEN(n) \
|
|
||||||
COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (INST_MATRIX_COLS(n)), \
|
|
||||||
(INST_MATRIX_ROWS(n)))
|
|
||||||
|
|
||||||
#define GPIO_INST_INIT(n) \
|
#if USE_INTERRUPTS
|
||||||
COND_INTERRUPTS( \
|
static int kscan_matrix_interrupt_disable(const struct device *dev) {
|
||||||
struct kscan_gpio_irq_callback_##n { \
|
int err = kscan_matrix_interrupt_configure(dev, GPIO_INT_DISABLE);
|
||||||
struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) * \
|
if (err) {
|
||||||
work; \
|
return err;
|
||||||
struct gpio_callback callback; \
|
}
|
||||||
const struct device *dev; \
|
|
||||||
}; \
|
// While interrupts are disabled, set all outputs inactive so
|
||||||
static struct kscan_gpio_irq_callback_##n irq_callbacks_##n[INST_INPUT_LEN(n)];) \
|
// kscan_matrix_read() can scan them one by one.
|
||||||
struct kscan_gpio_config_##n { \
|
return kscan_matrix_set_all_outputs(dev, 0);
|
||||||
struct kscan_gpio_item_config rows[INST_MATRIX_ROWS(n)]; \
|
}
|
||||||
struct kscan_gpio_item_config cols[INST_MATRIX_COLS(n)]; \
|
#endif
|
||||||
}; \
|
|
||||||
struct kscan_gpio_data_##n { \
|
#if USE_INTERRUPTS
|
||||||
kscan_callback_t callback; \
|
static void kscan_matrix_irq_callback_handler(const struct device *port, struct gpio_callback *cb,
|
||||||
COND_POLLING(struct k_timer poll_timer;) \
|
const gpio_port_pins_t pin) {
|
||||||
struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) work; \
|
struct kscan_matrix_irq_callback *data =
|
||||||
bool matrix_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \
|
CONTAINER_OF(cb, struct kscan_matrix_irq_callback, callback);
|
||||||
const struct device *rows[INST_MATRIX_ROWS(n)]; \
|
const struct kscan_matrix_config *config = data->dev->config;
|
||||||
const struct device *cols[INST_MATRIX_COLS(n)]; \
|
|
||||||
const struct device *dev; \
|
// Disable our interrupts temporarily to avoid re-entry while we scan.
|
||||||
}; \
|
kscan_matrix_interrupt_disable(data->dev);
|
||||||
static const struct device **kscan_gpio_input_devices_##n(const struct device *dev) { \
|
|
||||||
struct kscan_gpio_data_##n *data = dev->data; \
|
// TODO (Zephyr 2.6): use k_work_reschedule()
|
||||||
return (COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (data->cols), \
|
k_delayed_work_cancel(data->work);
|
||||||
(data->rows))); \
|
k_delayed_work_submit(data->work, K_MSEC(config->debounce_period_ms));
|
||||||
} \
|
}
|
||||||
static const struct kscan_gpio_item_config *kscan_gpio_input_configs_##n( \
|
#endif
|
||||||
const struct device *dev) { \
|
|
||||||
const struct kscan_gpio_config_##n *cfg = dev->config; \
|
static int kscan_matrix_read(const struct device *dev) {
|
||||||
return (( \
|
struct kscan_matrix_data *data = dev->data;
|
||||||
COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (cfg->cols), (cfg->rows)))); \
|
const struct kscan_matrix_config *config = dev->config;
|
||||||
} \
|
|
||||||
static const struct device **kscan_gpio_output_devices_##n(const struct device *dev) { \
|
// Scan the matrix.
|
||||||
struct kscan_gpio_data_##n *data = dev->data; \
|
for (int o = 0; o < config->outputs.len; o++) {
|
||||||
return (COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (data->rows), \
|
const struct kscan_gpio_dt_spec *out_gpio = &config->outputs.gpios[o];
|
||||||
(data->cols))); \
|
|
||||||
} \
|
int err = gpio_pin_set(out_gpio->port, out_gpio->pin, 1);
|
||||||
static const struct kscan_gpio_item_config *kscan_gpio_output_configs_##n( \
|
if (err) {
|
||||||
const struct device *dev) { \
|
LOG_ERR("Failed to set output %i active: %i", o, err);
|
||||||
const struct kscan_gpio_config_##n *cfg = dev->config; \
|
return err;
|
||||||
return ( \
|
}
|
||||||
COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (cfg->rows), (cfg->cols))); \
|
|
||||||
} \
|
for (int i = 0; i < config->inputs.len; i++) {
|
||||||
COND_INTERRUPTS( \
|
const struct kscan_gpio_dt_spec *in_gpio = &config->inputs.gpios[i];
|
||||||
static int kscan_gpio_enable_interrupts_##n(const struct device *dev) { \
|
|
||||||
return kscan_gpio_config_interrupts(kscan_gpio_input_devices_##n(dev), \
|
const int index = state_index_io(config, i, o);
|
||||||
kscan_gpio_input_configs_##n(dev), \
|
data->next_state[index] = gpio_pin_get(in_gpio->port, in_gpio->pin);
|
||||||
INST_INPUT_LEN(n), GPIO_INT_LEVEL_ACTIVE); \
|
}
|
||||||
} static int kscan_gpio_disable_interrupts_##n(const struct device *dev) { \
|
|
||||||
return kscan_gpio_config_interrupts(kscan_gpio_input_devices_##n(dev), \
|
err = gpio_pin_set(out_gpio->port, out_gpio->pin, 0);
|
||||||
kscan_gpio_input_configs_##n(dev), \
|
if (err) {
|
||||||
INST_INPUT_LEN(n), GPIO_INT_DISABLE); \
|
LOG_ERR("Failed to set output %i inactive: %i", o, err);
|
||||||
}) \
|
return err;
|
||||||
static void kscan_gpio_set_output_state_##n(const struct device *dev, int value) { \
|
}
|
||||||
int err; \
|
}
|
||||||
for (int i = 0; i < INST_OUTPUT_LEN(n); i++) { \
|
|
||||||
const struct device *in_dev = kscan_gpio_output_devices_##n(dev)[i]; \
|
// Process the new state.
|
||||||
const struct kscan_gpio_item_config *cfg = &kscan_gpio_output_configs_##n(dev)[i]; \
|
#if USE_INTERRUPTS
|
||||||
if ((err = gpio_pin_set(in_dev, cfg->pin, value))) { \
|
bool submit_followup_read = false;
|
||||||
LOG_DBG("FAILED TO SET OUTPUT %d to %d", cfg->pin, err); \
|
#endif
|
||||||
} \
|
|
||||||
} \
|
for (int r = 0; r < config->rows.len; r++) {
|
||||||
} \
|
for (int c = 0; c < config->cols.len; c++) {
|
||||||
static void kscan_gpio_set_matrix_state_##n( \
|
const int index = state_index_rc(config, r, c);
|
||||||
bool state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)], uint32_t input_index, \
|
const bool pressed = data->next_state[index];
|
||||||
uint32_t output_index, bool value) { \
|
|
||||||
state[COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (output_index), \
|
// Follow up reads are needed if any key is pressed because further
|
||||||
(input_index))] \
|
// interrupts won't fire on already tripped GPIO pins.
|
||||||
[COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (input_index), \
|
#if USE_INTERRUPTS
|
||||||
(output_index))] = value; \
|
submit_followup_read = submit_followup_read || pressed;
|
||||||
} \
|
#endif
|
||||||
static int kscan_gpio_read_##n(const struct device *dev) { \
|
if (pressed != data->current_state[index]) {
|
||||||
COND_INTERRUPTS(bool submit_follow_up_read = false;) \
|
LOG_DBG("Sending event at %i,%i state %s", r, c, pressed ? "on" : "off");
|
||||||
struct kscan_gpio_data_##n *data = dev->data; \
|
data->current_state[index] = pressed;
|
||||||
static bool read_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \
|
data->callback(dev, r, c, pressed);
|
||||||
int err; \
|
}
|
||||||
/* Disable our interrupts temporarily while we scan, to avoid */ \
|
}
|
||||||
/* re-entry while we iterate columns and set them active one by one */ \
|
}
|
||||||
/* to get pressed state for each matrix cell. */ \
|
|
||||||
COND_INTERRUPTS(kscan_gpio_set_output_state_##n(dev, 0);) \
|
#if USE_INTERRUPTS
|
||||||
for (int o = 0; o < INST_OUTPUT_LEN(n); o++) { \
|
if (submit_followup_read) {
|
||||||
const struct device *out_dev = kscan_gpio_output_devices_##n(dev)[o]; \
|
// At least one key is pressed. Poll until everything is released.
|
||||||
const struct kscan_gpio_item_config *out_cfg = &kscan_gpio_output_configs_##n(dev)[o]; \
|
// TODO (Zephyr 2.6): use k_work_reschedule()
|
||||||
err = gpio_pin_set(out_dev, out_cfg->pin, 1); \
|
k_delayed_work_cancel(&data->work);
|
||||||
if (err) { \
|
k_delayed_work_submit(&data->work, K_MSEC(config->debounce_period_ms));
|
||||||
LOG_ERR("Failed to set output active (err %d)", err); \
|
} else {
|
||||||
return err; \
|
// All keys are released. Return to waiting for an interrupt.
|
||||||
} \
|
kscan_matrix_interrupt_enable(dev);
|
||||||
for (int i = 0; i < INST_INPUT_LEN(n); i++) { \
|
}
|
||||||
const struct device *in_dev = kscan_gpio_input_devices_##n(dev)[i]; \
|
#endif
|
||||||
const struct kscan_gpio_item_config *in_cfg = \
|
|
||||||
&kscan_gpio_input_configs_##n(dev)[i]; \
|
return 0;
|
||||||
kscan_gpio_set_matrix_state_##n(read_state, i, o, \
|
}
|
||||||
gpio_pin_get(in_dev, in_cfg->pin) > 0); \
|
|
||||||
} \
|
#if USE_POLLING
|
||||||
err = gpio_pin_set(out_dev, out_cfg->pin, 0); \
|
static void kscan_matrix_timer_handler(struct k_timer *timer) {
|
||||||
if (err) { \
|
struct kscan_matrix_data *data = CONTAINER_OF(timer, struct kscan_matrix_data, poll_timer);
|
||||||
LOG_ERR("Failed to set output inactive (err %d)", err); \
|
k_delayed_work_submit(&data->work, K_NO_WAIT);
|
||||||
return err; \
|
}
|
||||||
} \
|
#endif
|
||||||
} \
|
|
||||||
/* Set all our outputs as active again. */ \
|
static void kscan_matrix_work_handler(struct k_work *work) {
|
||||||
COND_INTERRUPTS(kscan_gpio_set_output_state_##n(dev, 1);) \
|
struct k_delayed_work *dwork = CONTAINER_OF(work, struct k_delayed_work, work);
|
||||||
for (int r = 0; r < INST_MATRIX_ROWS(n); r++) { \
|
struct kscan_matrix_data *data = CONTAINER_OF(dwork, struct kscan_matrix_data, work);
|
||||||
for (int c = 0; c < INST_MATRIX_COLS(n); c++) { \
|
kscan_matrix_read(data->dev);
|
||||||
bool pressed = read_state[r][c]; \
|
}
|
||||||
/* Follow up reads needed because further interrupts won't fire on already tripped \
|
|
||||||
* input GPIO pins */ \
|
static int kscan_matrix_configure(const struct device *dev, const kscan_callback_t callback) {
|
||||||
COND_INTERRUPTS(submit_follow_up_read = (submit_follow_up_read || pressed);) \
|
struct kscan_matrix_data *data = dev->data;
|
||||||
if (pressed != data->matrix_state[r][c]) { \
|
|
||||||
LOG_DBG("Sending event at %d,%d state %s", r, c, (pressed ? "on" : "off")); \
|
if (!callback) {
|
||||||
data->matrix_state[r][c] = pressed; \
|
return -EINVAL;
|
||||||
data->callback(dev, r, c, pressed); \
|
}
|
||||||
} \
|
|
||||||
} \
|
data->callback = callback;
|
||||||
} \
|
return 0;
|
||||||
COND_INTERRUPTS( \
|
}
|
||||||
if (submit_follow_up_read) { \
|
|
||||||
COND_CODE_0(DT_INST_PROP(n, debounce_period), ({ k_work_submit(&data->work); }), \
|
static int kscan_matrix_enable(const struct device *dev) {
|
||||||
({ \
|
#if USE_POLLING
|
||||||
k_delayed_work_cancel(&data->work); \
|
struct kscan_matrix_data *data = dev->data;
|
||||||
k_delayed_work_submit(&data->work, K_MSEC(5)); \
|
const struct kscan_matrix_config *config = dev->config;
|
||||||
})) \
|
|
||||||
} else { kscan_gpio_enable_interrupts_##n(dev); }) \
|
k_timer_start(&data->poll_timer, K_MSEC(config->poll_period_ms),
|
||||||
return 0; \
|
K_MSEC(config->poll_period_ms));
|
||||||
} \
|
return 0;
|
||||||
static void kscan_gpio_work_handler_##n(struct k_work *work) { \
|
#else
|
||||||
struct kscan_gpio_data_##n *data = CONTAINER_OF(work, struct kscan_gpio_data_##n, work); \
|
// Read will automatically enable interrupts once done.
|
||||||
kscan_gpio_read_##n(data->dev); \
|
return kscan_matrix_read(dev);
|
||||||
} \
|
#endif
|
||||||
COND_INTERRUPTS(static void kscan_gpio_irq_callback_handler_##n( \
|
}
|
||||||
const struct device *dev, struct gpio_callback *cb, gpio_port_pins_t pin) { \
|
|
||||||
struct kscan_gpio_irq_callback_##n *data = \
|
static int kscan_matrix_disable(const struct device *dev) {
|
||||||
CONTAINER_OF(cb, struct kscan_gpio_irq_callback_##n, callback); \
|
#if USE_POLLING
|
||||||
kscan_gpio_disable_interrupts_##n(data->dev); \
|
struct kscan_matrix_data *data = dev->data;
|
||||||
COND_CODE_0(DT_INST_PROP(n, debounce_period), ({ k_work_submit(data->work); }), ({ \
|
|
||||||
k_delayed_work_cancel(data->work); \
|
k_timer_stop(&data->poll_timer);
|
||||||
k_delayed_work_submit(data->work, \
|
return 0;
|
||||||
K_MSEC(DT_INST_PROP(n, debounce_period))); \
|
#else
|
||||||
})) \
|
return kscan_matrix_interrupt_disable(dev);
|
||||||
}) \
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kscan_matrix_init_input_inst(const struct device *dev,
|
||||||
|
const struct kscan_gpio_dt_spec *gpio, const int index) {
|
||||||
|
if (!device_is_ready(gpio->port)) {
|
||||||
|
LOG_ERR("GPIO is not ready: %s", gpio->port->name);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = gpio_pin_configure(gpio->port, gpio->pin, GPIO_INPUT | gpio->dt_flags);
|
||||||
|
if (err) {
|
||||||
|
LOG_ERR("Unable to configure pin %u on %s for input", gpio->pin, gpio->port->name);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Configured pin %u on %s for input", gpio->pin, gpio->port->name);
|
||||||
|
|
||||||
|
#if USE_INTERRUPTS
|
||||||
|
struct kscan_matrix_data *data = dev->data;
|
||||||
|
struct kscan_matrix_irq_callback *irq = &data->irqs[index];
|
||||||
|
|
||||||
|
irq->dev = dev;
|
||||||
|
irq->work = &data->work;
|
||||||
|
gpio_init_callback(&irq->callback, kscan_matrix_irq_callback_handler, BIT(gpio->pin));
|
||||||
|
err = gpio_add_callback(gpio->port, &irq->callback);
|
||||||
|
if (err) {
|
||||||
|
LOG_ERR("Error adding the callback to the input device: %i", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kscan_matrix_init_inputs(const struct device *dev) {
|
||||||
|
const struct kscan_matrix_config *config = dev->config;
|
||||||
|
|
||||||
|
for (int i = 0; i < config->inputs.len; i++) {
|
||||||
|
const struct kscan_gpio_dt_spec *gpio = &config->inputs.gpios[i];
|
||||||
|
int err = kscan_matrix_init_input_inst(dev, gpio, i);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kscan_matrix_init_output_inst(const struct device *dev,
|
||||||
|
const struct kscan_gpio_dt_spec *gpio) {
|
||||||
|
if (!device_is_ready(gpio->port)) {
|
||||||
|
LOG_ERR("GPIO is not ready: %s", gpio->port->name);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = gpio_pin_configure(gpio->port, gpio->pin, GPIO_OUTPUT | gpio->dt_flags);
|
||||||
|
if (err) {
|
||||||
|
LOG_ERR("Unable to configure pin %u on %s for output", gpio->pin, gpio->port->name);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Configured pin %u on %s for output", gpio->pin, gpio->port->name);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kscan_matrix_init_outputs(const struct device *dev) {
|
||||||
|
const struct kscan_matrix_config *config = dev->config;
|
||||||
|
|
||||||
|
for (int i = 0; i < config->outputs.len; i++) {
|
||||||
|
const struct kscan_gpio_dt_spec *gpio = &config->outputs.gpios[i];
|
||||||
|
int err = kscan_matrix_init_output_inst(dev, gpio);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kscan_matrix_init(const struct device *dev) {
|
||||||
|
struct kscan_matrix_data *data = dev->data;
|
||||||
|
|
||||||
|
data->dev = dev;
|
||||||
|
|
||||||
|
kscan_matrix_init_inputs(dev);
|
||||||
|
kscan_matrix_init_outputs(dev);
|
||||||
|
kscan_matrix_set_all_outputs(dev, 0);
|
||||||
|
|
||||||
|
k_delayed_work_init(&data->work, kscan_matrix_work_handler);
|
||||||
|
|
||||||
|
#if USE_POLLING
|
||||||
|
k_timer_init(&data->poll_timer, kscan_matrix_timer_handler, NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct kscan_driver_api kscan_matrix_api = {
|
||||||
|
.config = kscan_matrix_configure,
|
||||||
|
.enable_callback = kscan_matrix_enable,
|
||||||
|
.disable_callback = kscan_matrix_disable,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define KSCAN_MATRIX_INIT(index) \
|
||||||
|
static const struct kscan_gpio_dt_spec kscan_matrix_rows_##index[] = { \
|
||||||
|
UTIL_LISTIFY(INST_ROWS_LEN(index), KSCAN_GPIO_ROW_CFG_INIT, index)}; \
|
||||||
\
|
\
|
||||||
static struct kscan_gpio_data_##n kscan_gpio_data_##n = { \
|
static const struct kscan_gpio_dt_spec kscan_matrix_cols_##index[] = { \
|
||||||
.rows = {[INST_MATRIX_ROWS(n) - 1] = NULL}, .cols = {[INST_MATRIX_COLS(n) - 1] = NULL}}; \
|
UTIL_LISTIFY(INST_COLS_LEN(index), KSCAN_GPIO_COL_CFG_INIT, index)}; \
|
||||||
static int kscan_gpio_configure_##n(const struct device *dev, kscan_callback_t callback) { \
|
\
|
||||||
struct kscan_gpio_data_##n *data = dev->data; \
|
static bool kscan_current_state_##index[INST_MATRIX_LEN(index)]; \
|
||||||
if (!callback) { \
|
static bool kscan_next_state_##index[INST_MATRIX_LEN(index)]; \
|
||||||
return -EINVAL; \
|
\
|
||||||
} \
|
COND_INTERRUPTS((static struct kscan_matrix_irq_callback \
|
||||||
data->callback = callback; \
|
kscan_matrix_irqs_##index[INST_INPUTS_LEN(index)];)) \
|
||||||
LOG_DBG("Configured GPIO %d", n); \
|
\
|
||||||
return 0; \
|
static struct kscan_matrix_data kscan_matrix_data_##index = { \
|
||||||
|
.current_state = kscan_current_state_##index, \
|
||||||
|
.next_state = kscan_next_state_##index, \
|
||||||
|
COND_INTERRUPTS((.irqs = kscan_matrix_irqs_##index, ))}; \
|
||||||
|
\
|
||||||
|
static struct kscan_matrix_config kscan_matrix_config_##index = { \
|
||||||
|
.rows = KSCAN_GPIO_LIST(kscan_matrix_rows_##index), \
|
||||||
|
.cols = KSCAN_GPIO_LIST(kscan_matrix_cols_##index), \
|
||||||
|
.inputs = KSCAN_GPIO_LIST( \
|
||||||
|
COND_DIODE_DIR(index, (kscan_matrix_cols_##index), (kscan_matrix_rows_##index))), \
|
||||||
|
.outputs = KSCAN_GPIO_LIST( \
|
||||||
|
COND_DIODE_DIR(index, (kscan_matrix_rows_##index), (kscan_matrix_cols_##index))), \
|
||||||
|
.debounce_period_ms = DT_INST_PROP(index, debounce_period), \
|
||||||
|
.poll_period_ms = DT_INST_PROP(index, poll_period_ms), \
|
||||||
|
.diode_direction = INST_DIODE_DIR(index), \
|
||||||
}; \
|
}; \
|
||||||
static int kscan_gpio_enable_##n(const struct device *dev) { \
|
\
|
||||||
COND_POLL_OR_INTERRUPTS((struct kscan_gpio_data_##n *data = dev->data; \
|
DEVICE_DT_INST_DEFINE(index, &kscan_matrix_init, device_pm_control_nop, \
|
||||||
k_timer_start(&data->poll_timer, K_MSEC(10), K_MSEC(10)); \
|
&kscan_matrix_data_##index, &kscan_matrix_config_##index, APPLICATION, \
|
||||||
return 0;), \
|
CONFIG_APPLICATION_INIT_PRIORITY, &kscan_matrix_api);
|
||||||
(int err = kscan_gpio_enable_interrupts_##n(dev); \
|
|
||||||
if (err) { return err; } return kscan_gpio_read_##n(dev);)) \
|
|
||||||
}; \
|
|
||||||
static int kscan_gpio_disable_##n(const struct device *dev) { \
|
|
||||||
COND_POLL_OR_INTERRUPTS((struct kscan_gpio_data_##n *data = dev->data; \
|
|
||||||
k_timer_stop(&data->poll_timer); return 0;), \
|
|
||||||
(return kscan_gpio_disable_interrupts_##n(dev);)) \
|
|
||||||
}; \
|
|
||||||
COND_POLLING(static void kscan_gpio_timer_handler_##n(struct k_timer *timer) { \
|
|
||||||
struct kscan_gpio_data_##n *data = \
|
|
||||||
CONTAINER_OF(timer, struct kscan_gpio_data_##n, poll_timer); \
|
|
||||||
k_work_submit(&data->work.work); \
|
|
||||||
}) \
|
|
||||||
static int kscan_gpio_init_##n(const struct device *dev) { \
|
|
||||||
struct kscan_gpio_data_##n *data = dev->data; \
|
|
||||||
int err; \
|
|
||||||
const struct device **input_devices = kscan_gpio_input_devices_##n(dev); \
|
|
||||||
for (int i = 0; i < INST_INPUT_LEN(n); i++) { \
|
|
||||||
const struct kscan_gpio_item_config *in_cfg = &kscan_gpio_input_configs_##n(dev)[i]; \
|
|
||||||
input_devices[i] = device_get_binding(in_cfg->label); \
|
|
||||||
if (!input_devices[i]) { \
|
|
||||||
LOG_ERR("Unable to find input GPIO device"); \
|
|
||||||
return -EINVAL; \
|
|
||||||
} \
|
|
||||||
err = gpio_pin_configure(input_devices[i], in_cfg->pin, GPIO_INPUT | in_cfg->flags); \
|
|
||||||
if (err) { \
|
|
||||||
LOG_ERR("Unable to configure pin %d on %s for input", in_cfg->pin, in_cfg->label); \
|
|
||||||
return err; \
|
|
||||||
} else { \
|
|
||||||
LOG_DBG("Configured pin %d on %s for input", in_cfg->pin, in_cfg->label); \
|
|
||||||
} \
|
|
||||||
COND_INTERRUPTS( \
|
|
||||||
irq_callbacks_##n[i].work = &data->work; irq_callbacks_##n[i].dev = dev; \
|
|
||||||
gpio_init_callback(&irq_callbacks_##n[i].callback, \
|
|
||||||
kscan_gpio_irq_callback_handler_##n, BIT(in_cfg->pin)); \
|
|
||||||
err = gpio_add_callback(input_devices[i], &irq_callbacks_##n[i].callback); \
|
|
||||||
if (err) { \
|
|
||||||
LOG_ERR("Error adding the callback to the input device"); \
|
|
||||||
return err; \
|
|
||||||
}) \
|
|
||||||
} \
|
|
||||||
const struct device **output_devices = kscan_gpio_output_devices_##n(dev); \
|
|
||||||
for (int o = 0; o < INST_OUTPUT_LEN(n); o++) { \
|
|
||||||
const struct kscan_gpio_item_config *out_cfg = &kscan_gpio_output_configs_##n(dev)[o]; \
|
|
||||||
output_devices[o] = device_get_binding(out_cfg->label); \
|
|
||||||
if (!output_devices[o]) { \
|
|
||||||
LOG_ERR("Unable to find output GPIO device"); \
|
|
||||||
return -EINVAL; \
|
|
||||||
} \
|
|
||||||
err = \
|
|
||||||
gpio_pin_configure(output_devices[o], out_cfg->pin, GPIO_OUTPUT | out_cfg->flags); \
|
|
||||||
if (err) { \
|
|
||||||
LOG_ERR("Unable to configure pin %d on %s for output", out_cfg->pin, \
|
|
||||||
out_cfg->label); \
|
|
||||||
return err; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
data->dev = dev; \
|
|
||||||
(COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work_init), (k_delayed_work_init)))( \
|
|
||||||
&data->work, kscan_gpio_work_handler_##n); \
|
|
||||||
COND_POLL_OR_INTERRUPTS( \
|
|
||||||
(k_timer_init(&data->poll_timer, kscan_gpio_timer_handler_##n, NULL); \
|
|
||||||
kscan_gpio_set_output_state_##n(dev, 0);), \
|
|
||||||
(kscan_gpio_set_output_state_##n(dev, 1);)) \
|
|
||||||
return 0; \
|
|
||||||
} \
|
|
||||||
static const struct kscan_driver_api gpio_driver_api_##n = { \
|
|
||||||
.config = kscan_gpio_configure_##n, \
|
|
||||||
.enable_callback = kscan_gpio_enable_##n, \
|
|
||||||
.disable_callback = kscan_gpio_disable_##n, \
|
|
||||||
}; \
|
|
||||||
static const struct kscan_gpio_config_##n kscan_gpio_config_##n = { \
|
|
||||||
.rows = {UTIL_LISTIFY(INST_MATRIX_ROWS(n), _KSCAN_GPIO_ROW_CFG_INIT, n)}, \
|
|
||||||
.cols = {UTIL_LISTIFY(INST_MATRIX_COLS(n), _KSCAN_GPIO_COL_CFG_INIT, n)}, \
|
|
||||||
}; \
|
|
||||||
DEVICE_AND_API_INIT(kscan_gpio_##n, DT_INST_LABEL(n), kscan_gpio_init_##n, \
|
|
||||||
&kscan_gpio_data_##n, &kscan_gpio_config_##n, APPLICATION, \
|
|
||||||
CONFIG_APPLICATION_INIT_PRIORITY, &gpio_driver_api_##n);
|
|
||||||
|
|
||||||
DT_INST_FOREACH_STATUS_OKAY(GPIO_INST_INIT)
|
DT_INST_FOREACH_STATUS_OKAY(KSCAN_MATRIX_INIT);
|
||||||
|
|
||||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
#endif // DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||||
|
|
|
@ -88,8 +88,8 @@ static int kscan_mock_configure(const struct device *dev, kscan_callback_t callb
|
||||||
static struct kscan_mock_data kscan_mock_data_##n; \
|
static struct kscan_mock_data kscan_mock_data_##n; \
|
||||||
static const struct kscan_mock_config_##n kscan_mock_config_##n = { \
|
static const struct kscan_mock_config_##n kscan_mock_config_##n = { \
|
||||||
.events = DT_INST_PROP(n, events), .exit_after = DT_INST_PROP(n, exit_after)}; \
|
.events = DT_INST_PROP(n, events), .exit_after = DT_INST_PROP(n, exit_after)}; \
|
||||||
DEVICE_AND_API_INIT(kscan_mock_##n, DT_INST_LABEL(n), kscan_mock_init_##n, \
|
DEVICE_DT_INST_DEFINE(n, kscan_mock_init_##n, device_pm_control_nop, &kscan_mock_data_##n, \
|
||||||
&kscan_mock_data_##n, &kscan_mock_config_##n, APPLICATION, \
|
&kscan_mock_config_##n, APPLICATION, \
|
||||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &mock_driver_api_##n);
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &mock_driver_api_##n);
|
||||||
|
|
||||||
DT_INST_FOREACH_STATUS_OKAY(MOCK_INST_INIT)
|
DT_INST_FOREACH_STATUS_OKAY(MOCK_INST_INIT)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Copyright (c) 2020 The ZMK Contributors
|
# Copyright (c) 2020-2021 The ZMK Contributors
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
add_subdirectory_ifdef(CONFIG_ZMK_BATTERY_VOLTAGE_DIVIDER battery_voltage_divider)
|
add_subdirectory_ifdef(CONFIG_ZMK_BATTERY battery)
|
||||||
add_subdirectory_ifdef(CONFIG_EC11 ec11)
|
add_subdirectory_ifdef(CONFIG_EC11 ec11)
|
|
@ -1,5 +1,5 @@
|
||||||
# Copyright (c) 2020 The ZMK Contributors
|
# Copyright (c) 2020 The ZMK Contributors
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
rsource "battery_voltage_divider/Kconfig"
|
rsource "battery/Kconfig"
|
||||||
rsource "ec11/Kconfig"
|
rsource "ec11/Kconfig"
|
10
app/drivers/sensor/battery/CMakeLists.txt
Normal file
10
app/drivers/sensor/battery/CMakeLists.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# Copyright (c) 2020-2021 The ZMK Contributors
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
zephyr_include_directories(.)
|
||||||
|
|
||||||
|
zephyr_library()
|
||||||
|
|
||||||
|
zephyr_library_sources(battery_common.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_ZMK_BATTERY_NRF_VDDH battery_nrf_vddh.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_ZMK_BATTERY_VOLTAGE_DIVIDER battery_voltage_divider.c)
|
21
app/drivers/sensor/battery/Kconfig
Normal file
21
app/drivers/sensor/battery/Kconfig
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# Copyright (c) 2020-2021 The ZMK Contributors
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
config ZMK_BATTERY
|
||||||
|
bool "ZMK battery monitoring"
|
||||||
|
help
|
||||||
|
Enable battery monitoring
|
||||||
|
|
||||||
|
config ZMK_BATTERY_NRF_VDDH
|
||||||
|
bool "ZMK nRF VDDH battery monitoring"
|
||||||
|
select ADC
|
||||||
|
select ZMK_BATTERY
|
||||||
|
help
|
||||||
|
Enable ZMK nRF VDDH voltage driver for battery monitoring.
|
||||||
|
|
||||||
|
config ZMK_BATTERY_VOLTAGE_DIVIDER
|
||||||
|
bool "ZMK battery voltage divider"
|
||||||
|
select ADC
|
||||||
|
select ZMK_BATTERY
|
||||||
|
help
|
||||||
|
Enable ZMK battery voltage divider driver for battery monitoring.
|
43
app/drivers/sensor/battery/battery_common.c
Normal file
43
app/drivers/sensor/battery/battery_common.c
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <drivers/sensor.h>
|
||||||
|
|
||||||
|
#include "battery_common.h"
|
||||||
|
|
||||||
|
int battery_channel_get(const struct battery_value *value, enum sensor_channel chan,
|
||||||
|
struct sensor_value *val_out) {
|
||||||
|
switch (chan) {
|
||||||
|
case SENSOR_CHAN_GAUGE_VOLTAGE:
|
||||||
|
val_out->val1 = value->millivolts / 1000;
|
||||||
|
val_out->val2 = (value->millivolts % 1000) * 1000U;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE:
|
||||||
|
val_out->val1 = value->state_of_charge;
|
||||||
|
val_out->val2 = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t lithium_ion_mv_to_pct(int16_t bat_mv) {
|
||||||
|
// Simple linear approximation of a battery based off adafruit's discharge graph:
|
||||||
|
// https://learn.adafruit.com/li-ion-and-lipoly-batteries/voltages
|
||||||
|
|
||||||
|
if (bat_mv >= 4200) {
|
||||||
|
return 100;
|
||||||
|
} else if (bat_mv <= 3450) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bat_mv * 2 / 15 - 459;
|
||||||
|
}
|
21
app/drivers/sensor/battery/battery_common.h
Normal file
21
app/drivers/sensor/battery/battery_common.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <drivers/sensor.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct battery_value {
|
||||||
|
uint16_t adc_raw;
|
||||||
|
uint16_t millivolts;
|
||||||
|
uint8_t state_of_charge;
|
||||||
|
};
|
||||||
|
|
||||||
|
int battery_channel_get(const struct battery_value *value, enum sensor_channel chan,
|
||||||
|
struct sensor_value *val_out);
|
||||||
|
|
||||||
|
uint8_t lithium_ion_mv_to_pct(int16_t bat_mv);
|
116
app/drivers/sensor/battery/battery_nrf_vddh.c
Normal file
116
app/drivers/sensor/battery/battery_nrf_vddh.c
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* This is a simplified version of battery_voltage_divider.c which always reads
|
||||||
|
* the VDDHDIV5 channel of the &adc node and multiplies it by 5.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT zmk_battery_nrf_vddh
|
||||||
|
|
||||||
|
#include <device.h>
|
||||||
|
#include <devicetree.h>
|
||||||
|
#include <drivers/adc.h>
|
||||||
|
#include <drivers/sensor.h>
|
||||||
|
#include <logging/log.h>
|
||||||
|
|
||||||
|
#include "battery_common.h"
|
||||||
|
|
||||||
|
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
|
|
||||||
|
#define VDDHDIV (5)
|
||||||
|
|
||||||
|
static const struct device *adc = DEVICE_DT_GET(DT_NODELABEL(adc));
|
||||||
|
|
||||||
|
struct vddh_data {
|
||||||
|
struct adc_channel_cfg acc;
|
||||||
|
struct adc_sequence as;
|
||||||
|
struct battery_value value;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int vddh_sample_fetch(const struct device *dev, enum sensor_channel chan) {
|
||||||
|
// Make sure selected channel is supported
|
||||||
|
if (chan != SENSOR_CHAN_GAUGE_VOLTAGE && chan != SENSOR_CHAN_GAUGE_STATE_OF_CHARGE &&
|
||||||
|
chan != SENSOR_CHAN_ALL) {
|
||||||
|
LOG_DBG("Selected channel is not supported: %d.", chan);
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct vddh_data *drv_data = dev->data;
|
||||||
|
struct adc_sequence *as = &drv_data->as;
|
||||||
|
|
||||||
|
int rc = adc_read(adc, as);
|
||||||
|
as->calibrate = false;
|
||||||
|
|
||||||
|
if (rc != 0) {
|
||||||
|
LOG_ERR("Failed to read ADC: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t val = drv_data->value.adc_raw;
|
||||||
|
rc = adc_raw_to_millivolts(adc_ref_internal(adc), drv_data->acc.gain, as->resolution, &val);
|
||||||
|
if (rc != 0) {
|
||||||
|
LOG_ERR("Failed to convert raw ADC to mV: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
drv_data->value.millivolts = val * VDDHDIV;
|
||||||
|
drv_data->value.state_of_charge = lithium_ion_mv_to_pct(drv_data->value.millivolts);
|
||||||
|
|
||||||
|
LOG_DBG("ADC raw %d ~ %d mV => %d%%", drv_data->value.adc_raw, drv_data->value.millivolts,
|
||||||
|
drv_data->value.state_of_charge);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vddh_channel_get(const struct device *dev, enum sensor_channel chan,
|
||||||
|
struct sensor_value *val) {
|
||||||
|
struct vddh_data const *drv_data = dev->data;
|
||||||
|
return battery_channel_get(&drv_data->value, chan, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct sensor_driver_api vddh_api = {
|
||||||
|
.sample_fetch = vddh_sample_fetch,
|
||||||
|
.channel_get = vddh_channel_get,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int vddh_init(const struct device *dev) {
|
||||||
|
struct vddh_data *drv_data = dev->data;
|
||||||
|
|
||||||
|
if (!device_is_ready(adc)) {
|
||||||
|
LOG_ERR("ADC device is not ready %s", adc->name);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
drv_data->as = (struct adc_sequence){
|
||||||
|
.channels = BIT(0),
|
||||||
|
.buffer = &drv_data->value.adc_raw,
|
||||||
|
.buffer_size = sizeof(drv_data->value.adc_raw),
|
||||||
|
.oversampling = 4,
|
||||||
|
.calibrate = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_ADC_NRFX_SAADC
|
||||||
|
drv_data->acc = (struct adc_channel_cfg){
|
||||||
|
.gain = ADC_GAIN_1_5,
|
||||||
|
.reference = ADC_REF_INTERNAL,
|
||||||
|
.acquisition_time = ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40),
|
||||||
|
.input_positive = SAADC_CH_PSELN_PSELN_VDDHDIV5,
|
||||||
|
};
|
||||||
|
|
||||||
|
drv_data->as.resolution = 12;
|
||||||
|
#else
|
||||||
|
#error Unsupported ADC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const int rc = adc_channel_setup(adc, &drv_data->acc);
|
||||||
|
LOG_DBG("VDDHDIV5 setup returned %d", rc);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct vddh_data vddh_data;
|
||||||
|
|
||||||
|
DEVICE_DT_INST_DEFINE(0, &vddh_init, device_pm_control_nop, &vddh_data, NULL, POST_KERNEL,
|
||||||
|
CONFIG_SENSOR_INIT_PRIORITY, &vddh_api);
|
|
@ -7,11 +7,14 @@
|
||||||
#define DT_DRV_COMPAT zmk_battery_voltage_divider
|
#define DT_DRV_COMPAT zmk_battery_voltage_divider
|
||||||
|
|
||||||
#include <device.h>
|
#include <device.h>
|
||||||
|
#include <devicetree.h>
|
||||||
#include <drivers/gpio.h>
|
#include <drivers/gpio.h>
|
||||||
#include <drivers/adc.h>
|
#include <drivers/adc.h>
|
||||||
#include <drivers/sensor.h>
|
#include <drivers/sensor.h>
|
||||||
#include <logging/log.h>
|
#include <logging/log.h>
|
||||||
|
|
||||||
|
#include "battery_common.h"
|
||||||
|
|
||||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
|
|
||||||
struct io_channel_config {
|
struct io_channel_config {
|
||||||
|
@ -37,24 +40,9 @@ struct bvd_data {
|
||||||
const struct device *gpio;
|
const struct device *gpio;
|
||||||
struct adc_channel_cfg acc;
|
struct adc_channel_cfg acc;
|
||||||
struct adc_sequence as;
|
struct adc_sequence as;
|
||||||
uint16_t adc_raw;
|
struct battery_value value;
|
||||||
uint16_t voltage;
|
|
||||||
uint8_t state_of_charge;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint8_t lithium_ion_mv_to_pct(int16_t bat_mv) {
|
|
||||||
// Simple linear approximation of a battery based off adafruit's discharge graph:
|
|
||||||
// https://learn.adafruit.com/li-ion-and-lipoly-batteries/voltages
|
|
||||||
|
|
||||||
if (bat_mv >= 4200) {
|
|
||||||
return 100;
|
|
||||||
} else if (bat_mv <= 3450) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bat_mv * 2 / 15 - 459;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bvd_sample_fetch(const struct device *dev, enum sensor_channel chan) {
|
static int bvd_sample_fetch(const struct device *dev, enum sensor_channel chan) {
|
||||||
struct bvd_data *drv_data = dev->data;
|
struct bvd_data *drv_data = dev->data;
|
||||||
const struct bvd_config *drv_cfg = dev->config;
|
const struct bvd_config *drv_cfg = dev->config;
|
||||||
|
@ -87,18 +75,18 @@ static int bvd_sample_fetch(const struct device *dev, enum sensor_channel chan)
|
||||||
as->calibrate = false;
|
as->calibrate = false;
|
||||||
|
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
int32_t val = drv_data->adc_raw;
|
int32_t val = drv_data->value.adc_raw;
|
||||||
|
|
||||||
adc_raw_to_millivolts(adc_ref_internal(drv_data->adc), drv_data->acc.gain, as->resolution,
|
adc_raw_to_millivolts(adc_ref_internal(drv_data->adc), drv_data->acc.gain, as->resolution,
|
||||||
&val);
|
&val);
|
||||||
|
|
||||||
uint16_t millivolts = val * (uint64_t)drv_cfg->full_ohm / drv_cfg->output_ohm;
|
uint16_t millivolts = val * (uint64_t)drv_cfg->full_ohm / drv_cfg->output_ohm;
|
||||||
LOG_DBG("ADC raw %d ~ %d mV => %d mV", drv_data->adc_raw, val, millivolts);
|
LOG_DBG("ADC raw %d ~ %d mV => %d mV", drv_data->value.adc_raw, val, millivolts);
|
||||||
uint8_t percent = lithium_ion_mv_to_pct(millivolts);
|
uint8_t percent = lithium_ion_mv_to_pct(millivolts);
|
||||||
LOG_DBG("Percent: %d", percent);
|
LOG_DBG("Percent: %d", percent);
|
||||||
|
|
||||||
drv_data->voltage = millivolts;
|
drv_data->value.millivolts = millivolts;
|
||||||
drv_data->state_of_charge = percent;
|
drv_data->value.state_of_charge = percent;
|
||||||
} else {
|
} else {
|
||||||
LOG_DBG("Failed to read ADC: %d", rc);
|
LOG_DBG("Failed to read ADC: %d", rc);
|
||||||
}
|
}
|
||||||
|
@ -119,23 +107,7 @@ static int bvd_sample_fetch(const struct device *dev, enum sensor_channel chan)
|
||||||
static int bvd_channel_get(const struct device *dev, enum sensor_channel chan,
|
static int bvd_channel_get(const struct device *dev, enum sensor_channel chan,
|
||||||
struct sensor_value *val) {
|
struct sensor_value *val) {
|
||||||
struct bvd_data *drv_data = dev->data;
|
struct bvd_data *drv_data = dev->data;
|
||||||
|
return battery_channel_get(&drv_data->value, chan, val);
|
||||||
switch (chan) {
|
|
||||||
case SENSOR_CHAN_GAUGE_VOLTAGE:
|
|
||||||
val->val1 = drv_data->voltage / 1000;
|
|
||||||
val->val2 = (drv_data->voltage % 1000) * 1000U;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE:
|
|
||||||
val->val1 = drv_data->state_of_charge;
|
|
||||||
val->val2 = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct sensor_driver_api bvd_api = {
|
static const struct sensor_driver_api bvd_api = {
|
||||||
|
@ -173,8 +145,8 @@ static int bvd_init(const struct device *dev) {
|
||||||
|
|
||||||
drv_data->as = (struct adc_sequence){
|
drv_data->as = (struct adc_sequence){
|
||||||
.channels = BIT(0),
|
.channels = BIT(0),
|
||||||
.buffer = &drv_data->adc_raw,
|
.buffer = &drv_data->value.adc_raw,
|
||||||
.buffer_size = sizeof(drv_data->adc_raw),
|
.buffer_size = sizeof(drv_data->value.adc_raw),
|
||||||
.oversampling = 4,
|
.oversampling = 4,
|
||||||
.calibrate = true,
|
.calibrate = true,
|
||||||
};
|
};
|
||||||
|
@ -217,5 +189,5 @@ static const struct bvd_config bvd_cfg = {
|
||||||
.full_ohm = DT_INST_PROP(0, full_ohms),
|
.full_ohm = DT_INST_PROP(0, full_ohms),
|
||||||
};
|
};
|
||||||
|
|
||||||
DEVICE_AND_API_INIT(bvd_dev, DT_INST_LABEL(0), &bvd_init, &bvd_data, &bvd_cfg, POST_KERNEL,
|
DEVICE_DT_INST_DEFINE(0, &bvd_init, device_pm_control_nop, &bvd_data, &bvd_cfg, POST_KERNEL,
|
||||||
CONFIG_SENSOR_INIT_PRIORITY, &bvd_api);
|
CONFIG_SENSOR_INIT_PRIORITY, &bvd_api);
|
|
@ -1,6 +0,0 @@
|
||||||
# Copyright (c) 2020 The ZMK Contributors
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
zephyr_library()
|
|
||||||
|
|
||||||
zephyr_library_sources(battery_voltage_divider.c)
|
|
|
@ -1,8 +0,0 @@
|
||||||
# Copyright (c) 2020 The ZMK Contributors
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
config ZMK_BATTERY_VOLTAGE_DIVIDER
|
|
||||||
bool "ZMK battery voltage divider"
|
|
||||||
select ADC
|
|
||||||
help
|
|
||||||
Enable ZMK battery voltage divider driver for battery monitoring.
|
|
|
@ -142,7 +142,7 @@ int ec11_init(const struct device *dev) {
|
||||||
.b_flags = DT_INST_GPIO_FLAGS(n, b_gpios), \
|
.b_flags = DT_INST_GPIO_FLAGS(n, b_gpios), \
|
||||||
COND_CODE_0(DT_INST_NODE_HAS_PROP(n, resolution), (1), (DT_INST_PROP(n, resolution))), \
|
COND_CODE_0(DT_INST_NODE_HAS_PROP(n, resolution), (1), (DT_INST_PROP(n, resolution))), \
|
||||||
}; \
|
}; \
|
||||||
DEVICE_AND_API_INIT(ec11_##n, DT_INST_LABEL(n), ec11_init, &ec11_data_##n, &ec11_cfg_##n, \
|
DEVICE_DT_INST_DEFINE(n, ec11_init, device_pm_control_nop, &ec11_data_##n, &ec11_cfg_##n, \
|
||||||
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &ec11_driver_api);
|
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &ec11_driver_api);
|
||||||
|
|
||||||
DT_INST_FOREACH_STATUS_OKAY(EC11_INST)
|
DT_INST_FOREACH_STATUS_OKAY(EC11_INST)
|
||||||
|
|
|
@ -17,6 +17,11 @@ properties:
|
||||||
debounce-period:
|
debounce-period:
|
||||||
type: int
|
type: int
|
||||||
default: 5
|
default: 5
|
||||||
|
description: Debounce time in milliseconds
|
||||||
|
poll-period-ms:
|
||||||
|
type: int
|
||||||
|
default: 10
|
||||||
|
description: Time between reads in milliseconds when polling is enabled
|
||||||
diode-direction:
|
diode-direction:
|
||||||
type: string
|
type: string
|
||||||
default: row2col
|
default: row2col
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Copyright (c) 2021 The ZMK Contributors
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
description: Battery SoC monitoring using nRF VDDH
|
||||||
|
|
||||||
|
compatible: "zmk,battery-nrf-vddh"
|
||||||
|
|
||||||
|
properties:
|
||||||
|
label:
|
||||||
|
required: true
|
||||||
|
type: string
|
61
app/dts/bindings/display/gooddisplay,il0323.yaml
Normal file
61
app/dts/bindings/display/gooddisplay,il0323.yaml
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
# Copyright (c) 2020, Phytec Messtechnik GmbH, Peter Johanson
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
description: IL0323 EPD display controller
|
||||||
|
|
||||||
|
compatible: "gooddisplay,il0323"
|
||||||
|
|
||||||
|
include: spi-device.yaml
|
||||||
|
|
||||||
|
properties:
|
||||||
|
height:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: Height in pixel of the panel driven by the controller
|
||||||
|
|
||||||
|
width:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: Width in pixel of the panel driven by the controller
|
||||||
|
|
||||||
|
reset-gpios:
|
||||||
|
type: phandle-array
|
||||||
|
required: true
|
||||||
|
description: RESET pin.
|
||||||
|
|
||||||
|
The RESET pin of GD7965 is active low.
|
||||||
|
If connected directly the MCU pin should be configured
|
||||||
|
as active low.
|
||||||
|
|
||||||
|
dc-gpios:
|
||||||
|
type: phandle-array
|
||||||
|
required: true
|
||||||
|
description: DC pin.
|
||||||
|
|
||||||
|
The DC pin of GD7965 is active low (transmission command byte).
|
||||||
|
If connected directly the MCU pin should be configured
|
||||||
|
as active low.
|
||||||
|
|
||||||
|
busy-gpios:
|
||||||
|
type: phandle-array
|
||||||
|
required: true
|
||||||
|
description: BUSY pin.
|
||||||
|
|
||||||
|
The BUSY pin of GD7965 is active low.
|
||||||
|
If connected directly the MCU pin should be configured
|
||||||
|
as active low.
|
||||||
|
|
||||||
|
pwr:
|
||||||
|
type: uint8-array
|
||||||
|
required: true
|
||||||
|
description: Power Setting (PWR) values
|
||||||
|
|
||||||
|
cdi:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: VCOM and data interval value
|
||||||
|
|
||||||
|
tcon:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: TCON setting value
|
|
@ -6,13 +6,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <zmk/keys.h>
|
#include <zmk/endpoints_types.h>
|
||||||
#include <zmk/hid.h>
|
|
||||||
|
|
||||||
enum zmk_endpoint {
|
|
||||||
ZMK_ENDPOINT_USB,
|
|
||||||
ZMK_ENDPOINT_BLE,
|
|
||||||
};
|
|
||||||
|
|
||||||
int zmk_endpoints_select(enum zmk_endpoint endpoint);
|
int zmk_endpoints_select(enum zmk_endpoint endpoint);
|
||||||
int zmk_endpoints_toggle();
|
int zmk_endpoints_toggle();
|
||||||
|
|
12
app/include/zmk/endpoints_types.h
Normal file
12
app/include/zmk/endpoints_types.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
enum zmk_endpoint {
|
||||||
|
ZMK_ENDPOINT_USB,
|
||||||
|
ZMK_ENDPOINT_BLE,
|
||||||
|
};
|
18
app/include/zmk/events/endpoint_selection_changed.h
Normal file
18
app/include/zmk/events/endpoint_selection_changed.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <zephyr.h>
|
||||||
|
|
||||||
|
#include <zmk/endpoints_types.h>
|
||||||
|
#include <zmk/event_manager.h>
|
||||||
|
|
||||||
|
struct zmk_endpoint_selection_changed {
|
||||||
|
enum zmk_endpoint endpoint;
|
||||||
|
};
|
||||||
|
|
||||||
|
ZMK_EVENT_DECLARE(zmk_endpoint_selection_changed);
|
|
@ -7,6 +7,7 @@
|
||||||
#include <device.h>
|
#include <device.h>
|
||||||
#include <init.h>
|
#include <init.h>
|
||||||
#include <kernel.h>
|
#include <kernel.h>
|
||||||
|
#include <power/power.h>
|
||||||
|
|
||||||
#include <logging/log.h>
|
#include <logging/log.h>
|
||||||
|
|
||||||
|
@ -55,6 +56,8 @@ void activity_work_handler(struct k_work *work) {
|
||||||
int32_t inactive_time = current - activity_last_uptime;
|
int32_t inactive_time = current - activity_last_uptime;
|
||||||
#if IS_ENABLED(CONFIG_ZMK_SLEEP)
|
#if IS_ENABLED(CONFIG_ZMK_SLEEP)
|
||||||
if (inactive_time > MAX_SLEEP_MS) {
|
if (inactive_time > MAX_SLEEP_MS) {
|
||||||
|
// Put devices in low power mode before sleeping
|
||||||
|
pm_power_state_force((struct pm_state_info){PM_STATE_STANDBY, 0, 0});
|
||||||
set_state(ZMK_ACTIVITY_SLEEP);
|
set_state(ZMK_ACTIVITY_SLEEP);
|
||||||
} else
|
} else
|
||||||
#endif /* IS_ENABLED(CONFIG_ZMK_SLEEP) */
|
#endif /* IS_ENABLED(CONFIG_ZMK_SLEEP) */
|
||||||
|
|
|
@ -49,7 +49,7 @@ static const struct behavior_driver_api behavior_bt_driver_api = {
|
||||||
.binding_released = on_keymap_binding_released,
|
.binding_released = on_keymap_binding_released,
|
||||||
};
|
};
|
||||||
|
|
||||||
DEVICE_AND_API_INIT(behavior_bt, DT_INST_LABEL(0), behavior_bt_init, NULL, NULL, APPLICATION,
|
DEVICE_DT_INST_DEFINE(0, behavior_bt_init, device_pm_control_nop, NULL, NULL, APPLICATION,
|
||||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_bt_driver_api);
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_bt_driver_api);
|
||||||
|
|
||||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||||
|
|
|
@ -73,7 +73,7 @@ static const struct behavior_driver_api behavior_ext_power_driver_api = {
|
||||||
.binding_released = on_keymap_binding_released,
|
.binding_released = on_keymap_binding_released,
|
||||||
};
|
};
|
||||||
|
|
||||||
DEVICE_AND_API_INIT(behavior_ext_power, DT_INST_LABEL(0), behavior_ext_power_init, NULL, NULL,
|
DEVICE_DT_INST_DEFINE(0, behavior_ext_power_init, device_pm_control_nop, NULL, NULL, APPLICATION,
|
||||||
APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY, &behavior_ext_power_driver_api);
|
CONFIG_APPLICATION_INIT_PRIORITY, &behavior_ext_power_driver_api);
|
||||||
|
|
||||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||||
|
|
|
@ -487,11 +487,7 @@ static int on_hold_tap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||||
// if this behavior was queued we have to adjust the timer to only
|
// if this behavior was queued we have to adjust the timer to only
|
||||||
// wait for the remaining time.
|
// wait for the remaining time.
|
||||||
int32_t tapping_term_ms_left = (hold_tap->timestamp + cfg->tapping_term_ms) - k_uptime_get();
|
int32_t tapping_term_ms_left = (hold_tap->timestamp + cfg->tapping_term_ms) - k_uptime_get();
|
||||||
if (tapping_term_ms_left > 0) {
|
k_delayed_work_submit(&hold_tap->work, K_MSEC(tapping_term_ms_left));
|
||||||
k_delayed_work_submit(&hold_tap->work, K_MSEC(tapping_term_ms_left));
|
|
||||||
} else {
|
|
||||||
decide_hold_tap(hold_tap, HT_TIMER_EVENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ZMK_BEHAVIOR_OPAQUE;
|
return ZMK_BEHAVIOR_OPAQUE;
|
||||||
}
|
}
|
||||||
|
@ -652,10 +648,10 @@ static struct behavior_hold_tap_data behavior_hold_tap_data;
|
||||||
.hold_enabler_keys = DT_INST_PROP(n, hold_enabler_keys), \
|
.hold_enabler_keys = DT_INST_PROP(n, hold_enabler_keys), \
|
||||||
.hold_enabler_keys_len = DT_INST_PROP_LEN(n, hold_enabler_keys), \
|
.hold_enabler_keys_len = DT_INST_PROP_LEN(n, hold_enabler_keys), \
|
||||||
}; \
|
}; \
|
||||||
DEVICE_AND_API_INIT(behavior_hold_tap_##n, DT_INST_LABEL(n), behavior_hold_tap_init, \
|
DEVICE_DT_INST_DEFINE(n, behavior_hold_tap_init, device_pm_control_nop, \
|
||||||
&behavior_hold_tap_data, &behavior_hold_tap_config_##n, APPLICATION, \
|
&behavior_hold_tap_data, &behavior_hold_tap_config_##n, APPLICATION, \
|
||||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_hold_tap_driver_api);
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_hold_tap_driver_api);
|
||||||
|
|
||||||
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
||||||
|
|
||||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||||
|
|
|
@ -36,8 +36,8 @@ static const struct behavior_driver_api behavior_key_press_driver_api = {
|
||||||
.binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released};
|
.binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released};
|
||||||
|
|
||||||
#define KP_INST(n) \
|
#define KP_INST(n) \
|
||||||
DEVICE_AND_API_INIT(behavior_key_press_##n, DT_INST_LABEL(n), behavior_key_press_init, NULL, \
|
DEVICE_DT_INST_DEFINE(n, behavior_key_press_init, device_pm_control_nop, NULL, NULL, \
|
||||||
NULL, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
||||||
&behavior_key_press_driver_api);
|
&behavior_key_press_driver_api);
|
||||||
|
|
||||||
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
||||||
|
|
|
@ -90,10 +90,11 @@ static int behavior_mod_morph_init(const struct device *dev) { return 0; }
|
||||||
.mods = DT_INST_PROP(n, mods), \
|
.mods = DT_INST_PROP(n, mods), \
|
||||||
}; \
|
}; \
|
||||||
static struct behavior_mod_morph_data behavior_mod_morph_data_##n = {}; \
|
static struct behavior_mod_morph_data behavior_mod_morph_data_##n = {}; \
|
||||||
DEVICE_AND_API_INIT(behavior_mod_morph_##n, DT_INST_LABEL(n), behavior_mod_morph_init, \
|
DEVICE_DT_INST_DEFINE(n, behavior_mod_morph_init, device_pm_control_nop, \
|
||||||
&behavior_mod_morph_data_##n, &behavior_mod_morph_config_##n, APPLICATION, \
|
&behavior_mod_morph_data_##n, &behavior_mod_morph_config_##n, \
|
||||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mod_morph_driver_api);
|
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
||||||
|
&behavior_mod_morph_driver_api);
|
||||||
|
|
||||||
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -39,6 +39,6 @@ static const struct behavior_mo_config behavior_mo_config = {};
|
||||||
|
|
||||||
static struct behavior_mo_data behavior_mo_data;
|
static struct behavior_mo_data behavior_mo_data;
|
||||||
|
|
||||||
DEVICE_AND_API_INIT(behavior_mo, DT_INST_LABEL(0), behavior_mo_init, &behavior_mo_data,
|
DEVICE_DT_INST_DEFINE(0, behavior_mo_init, device_pm_control_nop, &behavior_mo_data,
|
||||||
&behavior_mo_config, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
|
&behavior_mo_config, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
|
||||||
&behavior_mo_driver_api);
|
&behavior_mo_driver_api);
|
||||||
|
|
|
@ -34,7 +34,7 @@ static const struct behavior_driver_api behavior_none_driver_api = {
|
||||||
.binding_released = on_keymap_binding_released,
|
.binding_released = on_keymap_binding_released,
|
||||||
};
|
};
|
||||||
|
|
||||||
DEVICE_AND_API_INIT(behavior_none, DT_INST_LABEL(0), behavior_none_init, NULL, NULL, APPLICATION,
|
DEVICE_DT_INST_DEFINE(0, behavior_none_init, device_pm_control_nop, NULL, NULL, APPLICATION,
|
||||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_none_driver_api);
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_none_driver_api);
|
||||||
|
|
||||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||||
|
|
|
@ -42,7 +42,7 @@ static const struct behavior_driver_api behavior_outputs_driver_api = {
|
||||||
.binding_pressed = on_keymap_binding_pressed,
|
.binding_pressed = on_keymap_binding_pressed,
|
||||||
};
|
};
|
||||||
|
|
||||||
DEVICE_AND_API_INIT(behavior_out, DT_INST_LABEL(0), behavior_out_init, NULL, NULL, APPLICATION,
|
DEVICE_DT_INST_DEFINE(0, behavior_out_init, device_pm_control_nop, NULL, NULL, APPLICATION,
|
||||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_outputs_driver_api);
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_outputs_driver_api);
|
||||||
|
|
||||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||||
|
|
|
@ -41,9 +41,9 @@ static const struct behavior_driver_api behavior_reset_driver_api = {
|
||||||
#define RST_INST(n) \
|
#define RST_INST(n) \
|
||||||
static const struct behavior_reset_config behavior_reset_config_##n = { \
|
static const struct behavior_reset_config behavior_reset_config_##n = { \
|
||||||
.type = DT_INST_PROP(n, type)}; \
|
.type = DT_INST_PROP(n, type)}; \
|
||||||
DEVICE_AND_API_INIT(behavior_reset_##n, DT_INST_LABEL(n), behavior_reset_init, NULL, \
|
DEVICE_DT_INST_DEFINE(n, behavior_reset_init, device_pm_control_nop, NULL, \
|
||||||
&behavior_reset_config_##n, APPLICATION, \
|
&behavior_reset_config_##n, APPLICATION, \
|
||||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_reset_driver_api);
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_reset_driver_api);
|
||||||
|
|
||||||
DT_INST_FOREACH_STATUS_OKAY(RST_INST)
|
DT_INST_FOREACH_STATUS_OKAY(RST_INST)
|
||||||
|
|
||||||
|
|
|
@ -136,8 +136,8 @@ static const struct behavior_driver_api behavior_rgb_underglow_driver_api = {
|
||||||
.binding_released = on_keymap_binding_released,
|
.binding_released = on_keymap_binding_released,
|
||||||
};
|
};
|
||||||
|
|
||||||
DEVICE_AND_API_INIT(behavior_rgb_underglow, DT_INST_LABEL(0), behavior_rgb_underglow_init, NULL,
|
DEVICE_DT_INST_DEFINE(0, behavior_rgb_underglow_init, device_pm_control_nop, NULL, NULL,
|
||||||
NULL, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
|
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
|
||||||
&behavior_rgb_underglow_driver_api);
|
&behavior_rgb_underglow_driver_api);
|
||||||
|
|
||||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||||
|
|
|
@ -59,10 +59,9 @@ static const struct behavior_driver_api behavior_sensor_rotate_key_press_driver_
|
||||||
.sensor_binding_triggered = on_sensor_binding_triggered};
|
.sensor_binding_triggered = on_sensor_binding_triggered};
|
||||||
|
|
||||||
#define KP_INST(n) \
|
#define KP_INST(n) \
|
||||||
DEVICE_AND_API_INIT(behavior_sensor_rotate_key_press_##n, DT_INST_LABEL(n), \
|
DEVICE_DT_INST_DEFINE(n, behavior_sensor_rotate_key_press_init, device_pm_control_nop, NULL, \
|
||||||
behavior_sensor_rotate_key_press_init, NULL, NULL, APPLICATION, \
|
NULL, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
||||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
&behavior_sensor_rotate_key_press_driver_api);
|
||||||
&behavior_sensor_rotate_key_press_driver_api);
|
|
||||||
|
|
||||||
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
||||||
|
|
||||||
|
|
|
@ -272,9 +272,9 @@ static struct behavior_sticky_key_data behavior_sticky_key_data;
|
||||||
.release_after_ms = DT_INST_PROP(n, release_after_ms), \
|
.release_after_ms = DT_INST_PROP(n, release_after_ms), \
|
||||||
.quick_release = DT_INST_PROP(n, quick_release), \
|
.quick_release = DT_INST_PROP(n, quick_release), \
|
||||||
}; \
|
}; \
|
||||||
DEVICE_AND_API_INIT(behavior_sticky_key_##n, DT_INST_LABEL(n), behavior_sticky_key_init, \
|
DEVICE_DT_INST_DEFINE(n, behavior_sticky_key_init, device_pm_control_nop, \
|
||||||
&behavior_sticky_key_data, &behavior_sticky_key_config_##n, APPLICATION, \
|
&behavior_sticky_key_data, &behavior_sticky_key_config_##n, APPLICATION, \
|
||||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_sticky_key_driver_api);
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_sticky_key_driver_api);
|
||||||
|
|
||||||
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ static const struct behavior_driver_api behavior_to_driver_api = {
|
||||||
.binding_released = to_keymap_binding_released,
|
.binding_released = to_keymap_binding_released,
|
||||||
};
|
};
|
||||||
|
|
||||||
DEVICE_AND_API_INIT(behavior_to, DT_INST_LABEL(0), behavior_to_init, NULL, NULL, APPLICATION,
|
DEVICE_DT_INST_DEFINE(0, behavior_to_init, device_pm_control_nop, NULL, NULL, APPLICATION,
|
||||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_to_driver_api);
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_to_driver_api);
|
||||||
|
|
||||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||||
|
|
|
@ -43,8 +43,8 @@ static const struct behavior_tog_config behavior_tog_config = {};
|
||||||
|
|
||||||
static struct behavior_tog_data behavior_tog_data;
|
static struct behavior_tog_data behavior_tog_data;
|
||||||
|
|
||||||
DEVICE_AND_API_INIT(behavior_tog, DT_INST_LABEL(0), behavior_tog_init, &behavior_tog_data,
|
DEVICE_DT_INST_DEFINE(0, behavior_tog_init, device_pm_control_nop, &behavior_tog_data,
|
||||||
&behavior_tog_config, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
|
&behavior_tog_config, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
|
||||||
&behavior_tog_driver_api);
|
&behavior_tog_driver_api);
|
||||||
|
|
||||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||||
|
|
|
@ -34,8 +34,7 @@ static const struct behavior_driver_api behavior_transparent_driver_api = {
|
||||||
.binding_released = on_keymap_binding_released,
|
.binding_released = on_keymap_binding_released,
|
||||||
};
|
};
|
||||||
|
|
||||||
DEVICE_AND_API_INIT(behavior_transparent, DT_INST_LABEL(0), behavior_transparent_init, NULL, NULL,
|
DEVICE_DT_INST_DEFINE(0, behavior_transparent_init, device_pm_control_nop, NULL, NULL, APPLICATION,
|
||||||
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_transparent_driver_api);
|
||||||
&behavior_transparent_driver_api);
|
|
||||||
|
|
||||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||||
|
|
|
@ -26,14 +26,16 @@ static lv_obj_t *screen;
|
||||||
|
|
||||||
__attribute__((weak)) lv_obj_t *zmk_display_status_screen() { return NULL; }
|
__attribute__((weak)) lv_obj_t *zmk_display_status_screen() { return NULL; }
|
||||||
|
|
||||||
void display_tick_cb(struct k_work *work) {
|
void display_tick_cb(struct k_work *work) { lv_task_handler(); }
|
||||||
lv_tick_inc(10);
|
|
||||||
lv_task_handler();
|
#define TICK_MS 10
|
||||||
}
|
|
||||||
|
|
||||||
K_WORK_DEFINE(display_tick_work, display_tick_cb);
|
K_WORK_DEFINE(display_tick_work, display_tick_cb);
|
||||||
|
|
||||||
void display_timer_cb() { k_work_submit(&display_tick_work); }
|
void display_timer_cb() {
|
||||||
|
lv_tick_inc(TICK_MS);
|
||||||
|
k_work_submit(&display_tick_work);
|
||||||
|
}
|
||||||
|
|
||||||
K_TIMER_DEFINE(display_timer, display_timer_cb, NULL);
|
K_TIMER_DEFINE(display_timer, display_timer_cb, NULL);
|
||||||
|
|
||||||
|
@ -44,7 +46,7 @@ static void start_display_updates() {
|
||||||
|
|
||||||
display_blanking_off(display);
|
display_blanking_off(display);
|
||||||
|
|
||||||
k_timer_start(&display_timer, K_MSEC(10), K_MSEC(10));
|
k_timer_start(&display_timer, K_MSEC(TICK_MS), K_MSEC(TICK_MS));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stop_display_updates() {
|
static void stop_display_updates() {
|
||||||
|
@ -75,8 +77,6 @@ int zmk_display_init() {
|
||||||
|
|
||||||
lv_scr_load(screen);
|
lv_scr_load(screen);
|
||||||
|
|
||||||
lv_task_handler();
|
|
||||||
|
|
||||||
start_display_updates();
|
start_display_updates();
|
||||||
|
|
||||||
LOG_DBG("");
|
LOG_DBG("");
|
||||||
|
|
|
@ -7,6 +7,7 @@ config ZMK_WIDGET_LAYER_STATUS
|
||||||
bool "Widget for highest, active layer using small icons"
|
bool "Widget for highest, active layer using small icons"
|
||||||
default y
|
default y
|
||||||
depends on !ZMK_SPLIT || ZMK_SPLIT_BLE_ROLE_CENTRAL
|
depends on !ZMK_SPLIT || ZMK_SPLIT_BLE_ROLE_CENTRAL
|
||||||
|
select LVGL_USE_LABEL
|
||||||
select LVGL_FONT_MONTSERRAT_12
|
select LVGL_FONT_MONTSERRAT_12
|
||||||
|
|
||||||
config ZMK_WIDGET_BATTERY_STATUS
|
config ZMK_WIDGET_BATTERY_STATUS
|
||||||
|
|
|
@ -13,6 +13,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
#include <zmk/event_manager.h>
|
#include <zmk/event_manager.h>
|
||||||
#include <zmk/events/usb_conn_state_changed.h>
|
#include <zmk/events/usb_conn_state_changed.h>
|
||||||
#include <zmk/events/ble_active_profile_changed.h>
|
#include <zmk/events/ble_active_profile_changed.h>
|
||||||
|
#include <zmk/events/endpoint_selection_changed.h>
|
||||||
#include <zmk/usb.h>
|
#include <zmk/usb.h>
|
||||||
#include <zmk/ble.h>
|
#include <zmk/ble.h>
|
||||||
#include <zmk/endpoints.h>
|
#include <zmk/endpoints.h>
|
||||||
|
@ -86,6 +87,8 @@ int output_status_listener(const zmk_event_t *eh) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ZMK_LISTENER(widget_output_status, output_status_listener)
|
ZMK_LISTENER(widget_output_status, output_status_listener)
|
||||||
|
ZMK_SUBSCRIPTION(widget_output_status, zmk_endpoint_selection_changed);
|
||||||
|
|
||||||
#if defined(CONFIG_USB)
|
#if defined(CONFIG_USB)
|
||||||
ZMK_SUBSCRIPTION(widget_output_status, zmk_usb_conn_state_changed);
|
ZMK_SUBSCRIPTION(widget_output_status, zmk_usb_conn_state_changed);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <zmk/event_manager.h>
|
#include <zmk/event_manager.h>
|
||||||
#include <zmk/events/ble_active_profile_changed.h>
|
#include <zmk/events/ble_active_profile_changed.h>
|
||||||
#include <zmk/events/usb_conn_state_changed.h>
|
#include <zmk/events/usb_conn_state_changed.h>
|
||||||
|
#include <zmk/events/endpoint_selection_changed.h>
|
||||||
|
|
||||||
#include <logging/log.h>
|
#include <logging/log.h>
|
||||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
|
@ -242,6 +243,9 @@ static void update_current_endpoint() {
|
||||||
|
|
||||||
current_endpoint = new_endpoint;
|
current_endpoint = new_endpoint;
|
||||||
LOG_INF("Endpoint changed: %d", current_endpoint);
|
LOG_INF("Endpoint changed: %d", current_endpoint);
|
||||||
|
|
||||||
|
ZMK_EVENT_RAISE(new_zmk_endpoint_selection_changed(
|
||||||
|
(struct zmk_endpoint_selection_changed){.endpoint = current_endpoint}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
app/src/events/endpoint_selection_changed.c
Normal file
10
app/src/events/endpoint_selection_changed.c
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The ZMK Contributors
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <zmk/events/endpoint_selection_changed.h>
|
||||||
|
|
||||||
|
ZMK_EVENT_IMPL(zmk_endpoint_selection_changed);
|
|
@ -32,7 +32,7 @@ struct ext_power_generic_data {
|
||||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||||
bool settings_init;
|
bool settings_init;
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
#ifdef CONFIG_PM_DEVICE
|
||||||
uint32_t pm_state;
|
uint32_t pm_state;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
@ -143,7 +143,7 @@ static int ext_power_generic_init(const struct device *dev) {
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
#ifdef CONFIG_PM_DEVICE
|
||||||
data->pm_state = DEVICE_PM_ACTIVE_STATE;
|
data->pm_state = DEVICE_PM_ACTIVE_STATE;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ static int ext_power_generic_init(const struct device *dev) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
#ifdef CONFIG_PM_DEVICE
|
||||||
static int ext_power_generic_pm_control(const struct device *dev, uint32_t ctrl_command,
|
static int ext_power_generic_pm_control(const struct device *dev, uint32_t ctrl_command,
|
||||||
void *context, device_pm_cb cb, void *arg) {
|
void *context, device_pm_cb cb, void *arg) {
|
||||||
int rc;
|
int rc;
|
||||||
|
@ -210,7 +210,7 @@ static int ext_power_generic_pm_control(const struct device *dev, uint32_t ctrl_
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_DEVICE_POWER_MANAGEMENT */
|
#endif /* CONFIG_PM_DEVICE */
|
||||||
|
|
||||||
static const struct ext_power_generic_config config = {
|
static const struct ext_power_generic_config config = {
|
||||||
.label = DT_INST_GPIO_LABEL(0, control_gpios),
|
.label = DT_INST_GPIO_LABEL(0, control_gpios),
|
||||||
|
@ -231,13 +231,7 @@ static const struct ext_power_api api = {.enable = ext_power_generic_enable,
|
||||||
|
|
||||||
#define ZMK_EXT_POWER_INIT_PRIORITY 81
|
#define ZMK_EXT_POWER_INIT_PRIORITY 81
|
||||||
|
|
||||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
DEVICE_DT_INST_DEFINE(0, ext_power_generic_init, &ext_power_generic_pm_control, &data, &config,
|
||||||
DEVICE_DEFINE(ext_power_generic, DT_INST_LABEL(0), ext_power_generic_init,
|
POST_KERNEL, ZMK_EXT_POWER_INIT_PRIORITY, &api);
|
||||||
&ext_power_generic_pm_control, &data, &config, POST_KERNEL,
|
|
||||||
ZMK_EXT_POWER_INIT_PRIORITY, &api);
|
|
||||||
#else
|
|
||||||
DEVICE_AND_API_INIT(ext_power_generic, DT_INST_LABEL(0), ext_power_generic_init, &data, &config,
|
|
||||||
POST_KERNEL, ZMK_EXT_POWER_INIT_PRIORITY, &api);
|
|
||||||
#endif /* CONFIG_DEVICE_POWER_MANAGEMENT */
|
|
||||||
|
|
||||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||||
|
|
|
@ -23,18 +23,12 @@ bool is_usb_power_present() {
|
||||||
#endif /* CONFIG_USB */
|
#endif /* CONFIG_USB */
|
||||||
}
|
}
|
||||||
|
|
||||||
enum power_states sys_pm_policy_next_state(int32_t ticks) {
|
struct pm_state_info pm_policy_next_state(int32_t ticks) {
|
||||||
#ifdef CONFIG_SYS_POWER_DEEP_SLEEP_STATES
|
|
||||||
#ifdef CONFIG_HAS_SYS_POWER_STATE_DEEP_SLEEP_1
|
|
||||||
if (zmk_activity_get_state() == ZMK_ACTIVITY_SLEEP && !is_usb_power_present()) {
|
if (zmk_activity_get_state() == ZMK_ACTIVITY_SLEEP && !is_usb_power_present()) {
|
||||||
return SYS_POWER_STATE_DEEP_SLEEP_1;
|
return (struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0};
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_HAS_SYS_POWER_STATE_DEEP_SLEEP_1 */
|
|
||||||
#endif /* CONFIG_SYS_POWER_DEEP_SLEEP_STATES */
|
|
||||||
|
|
||||||
return SYS_POWER_STATE_ACTIVE;
|
return (struct pm_state_info){PM_STATE_ACTIVE, 0, 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sys_pm_policy_low_power_devices(enum power_states pm_state) {
|
__weak bool pm_policy_low_power_devices(enum pm_state state) { return pm_is_sleep_state(state); }
|
||||||
return sys_pm_is_sleep_state(pm_state);
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,6 +19,13 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
|
|
||||||
static enum usb_dc_status_code usb_status = USB_DC_UNKNOWN;
|
static enum usb_dc_status_code usb_status = USB_DC_UNKNOWN;
|
||||||
|
|
||||||
|
static void raise_usb_status_changed_event(struct k_work *_work) {
|
||||||
|
ZMK_EVENT_RAISE(new_zmk_usb_conn_state_changed(
|
||||||
|
(struct zmk_usb_conn_state_changed){.conn_state = zmk_usb_get_conn_state()}));
|
||||||
|
}
|
||||||
|
|
||||||
|
K_WORK_DEFINE(usb_status_notifier_work, raise_usb_status_changed_event);
|
||||||
|
|
||||||
#ifdef CONFIG_ZMK_USB
|
#ifdef CONFIG_ZMK_USB
|
||||||
|
|
||||||
static const struct device *hid_dev;
|
static const struct device *hid_dev;
|
||||||
|
@ -54,14 +61,10 @@ int zmk_usb_hid_send_report(const uint8_t *report, size_t len) {
|
||||||
|
|
||||||
#endif /* CONFIG_ZMK_USB */
|
#endif /* CONFIG_ZMK_USB */
|
||||||
|
|
||||||
static void raise_usb_status_changed_event() {
|
|
||||||
ZMK_EVENT_RAISE(new_zmk_usb_conn_state_changed(
|
|
||||||
(struct zmk_usb_conn_state_changed){.conn_state = zmk_usb_get_conn_state()}));
|
|
||||||
}
|
|
||||||
|
|
||||||
enum usb_dc_status_code zmk_usb_get_status() { return usb_status; }
|
enum usb_dc_status_code zmk_usb_get_status() { return usb_status; }
|
||||||
|
|
||||||
enum zmk_usb_conn_state zmk_usb_get_conn_state() {
|
enum zmk_usb_conn_state zmk_usb_get_conn_state() {
|
||||||
|
LOG_DBG("state: %d", usb_status);
|
||||||
switch (usb_status) {
|
switch (usb_status) {
|
||||||
case USB_DC_DISCONNECTED:
|
case USB_DC_DISCONNECTED:
|
||||||
case USB_DC_UNKNOWN:
|
case USB_DC_UNKNOWN:
|
||||||
|
@ -78,7 +81,7 @@ enum zmk_usb_conn_state zmk_usb_get_conn_state() {
|
||||||
|
|
||||||
void usb_status_cb(enum usb_dc_status_code status, const uint8_t *params) {
|
void usb_status_cb(enum usb_dc_status_code status, const uint8_t *params) {
|
||||||
usb_status = status;
|
usb_status = status;
|
||||||
raise_usb_status_changed_event();
|
k_work_submit(&usb_status_notifier_work);
|
||||||
};
|
};
|
||||||
|
|
||||||
static int zmk_usb_init(const struct device *_arg) {
|
static int zmk_usb_init(const struct device *_arg) {
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
s/.*hid_listener_keycode/kp/p
|
||||||
|
s/.*mo_keymap_binding/mo/p
|
||||||
|
s/.*on_hold_tap_binding/ht_binding/p
|
||||||
|
s/.*decide_hold_tap/ht_decide/p
|
|
@ -0,0 +1,10 @@
|
||||||
|
ht_binding_pressed: 0 new undecided hold_tap
|
||||||
|
ht_decide: 0 decided hold-timer (tap-preferred decision moment timer)
|
||||||
|
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
|
||||||
|
ht_binding_pressed: 1 new undecided hold_tap
|
||||||
|
ht_decide: 1 decided tap (tap-preferred decision moment key-up)
|
||||||
|
kp_pressed: usage_page 0x07 keycode 0x0d implicit_mods 0x00 explicit_mods 0x00
|
||||||
|
kp_released: usage_page 0x07 keycode 0x0d implicit_mods 0x00 explicit_mods 0x00
|
||||||
|
ht_binding_released: 1 cleaning up hold-tap
|
||||||
|
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
|
||||||
|
ht_binding_released: 0 cleaning up hold-tap
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include <dt-bindings/zmk/keys.h>
|
||||||
|
#include <behaviors.dtsi>
|
||||||
|
#include <dt-bindings/zmk/kscan_mock.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A hold-tap with long tapping term is pressed first.
|
||||||
|
* A hold-tap with short tapping term is quickly tapped.
|
||||||
|
* The short tapping term hold-tap should 'tap', not 'hold'.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/ {
|
||||||
|
behaviors {
|
||||||
|
tp_short: short_tap {
|
||||||
|
compatible = "zmk,behavior-hold-tap";
|
||||||
|
label = "MOD_TAP_SHORT";
|
||||||
|
#binding-cells = <2>;
|
||||||
|
flavor = "tap-preferred";
|
||||||
|
tapping-term-ms = <100>;
|
||||||
|
quick-tap-ms = <200>;
|
||||||
|
bindings = <&kp>, <&kp>;
|
||||||
|
};
|
||||||
|
tp_long: long_tap {
|
||||||
|
compatible = "zmk,behavior-hold-tap";
|
||||||
|
label = "MOD_TAP_LONG";
|
||||||
|
#binding-cells = <2>;
|
||||||
|
flavor = "tap-preferred";
|
||||||
|
tapping-term-ms = <200>;
|
||||||
|
quick-tap-ms = <200>;
|
||||||
|
bindings = <&kp>, <&kp>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
keymap {
|
||||||
|
compatible = "zmk,keymap";
|
||||||
|
label ="Default keymap";
|
||||||
|
|
||||||
|
default_layer {
|
||||||
|
bindings = <
|
||||||
|
&tp_long LEFT_SHIFT F &tp_short LEFT_CONTROL J
|
||||||
|
&kp D &kp RIGHT_CONTROL>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
&kscan {
|
||||||
|
events = <
|
||||||
|
ZMK_MOCK_PRESS(0,0,20)
|
||||||
|
ZMK_MOCK_PRESS(0,1,20)
|
||||||
|
ZMK_MOCK_RELEASE(0,1,200)
|
||||||
|
ZMK_MOCK_RELEASE(0,0,10)
|
||||||
|
>;
|
||||||
|
};
|
|
@ -9,7 +9,7 @@ manifest:
|
||||||
projects:
|
projects:
|
||||||
- name: zephyr
|
- name: zephyr
|
||||||
remote: zmkfirmware
|
remote: zmkfirmware
|
||||||
revision: v2.4.0+zmk-fixes
|
revision: v2.5.0+zmk-fixes
|
||||||
clone-depth: 1
|
clone-depth: 1
|
||||||
import:
|
import:
|
||||||
# TODO: Rename once upstream offers option like `exclude` or `denylist`
|
# TODO: Rename once upstream offers option like `exclude` or `denylist`
|
||||||
|
|
75
docs/blog/2021-07-17-zephyr-2-5.md
Normal file
75
docs/blog/2021-07-17-zephyr-2-5.md
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
---
|
||||||
|
title: "Zephyr 2.5 Update"
|
||||||
|
author: Pete Johanson
|
||||||
|
author_title: Project Creator
|
||||||
|
author_url: https://gitlab.com/petejohanson
|
||||||
|
author_image_url: https://www.gravatar.com/avatar/2001ceff7e9dc753cf96fcb2e6f41110
|
||||||
|
tags: [firmware, zephyr, core]
|
||||||
|
---
|
||||||
|
|
||||||
|
I'm happy to announce that we have completed the [work](https://github.com/zmkfirmware/zmk/pull/736/) to upgrade ZMK to [Zephyr 2.5](https://docs.zephyrproject.org/2.5.0/releases/release-notes-2.5.html)!
|
||||||
|
|
||||||
|
A big part of this work was some _major_ refactors and improvements by [innovaker] to our [zmk-docker](https://github.com/zmkfirmware/zmk-docker/) Docker image and GH Actions automation.
|
||||||
|
|
||||||
|
- Faster build times with improved caching.
|
||||||
|
- Integration tests which automatically verify new images.
|
||||||
|
- PRs to the repo now build properly and run the tests as well.
|
||||||
|
- Build images for multiple target architectures, e.g. `zmk-build-riscv64`, all in parallel.
|
||||||
|
- Nightly builds to be sure we're pulling in the latest OS/package updates, to ensure we keep our images up to date, address any reported vulnerabilities, etc.
|
||||||
|
- Faster upgrade paths for future Zephyr SDK and Zephyr versions.
|
||||||
|
|
||||||
|
In addition, [petejohanson] did the upgrade work to adjust ZMK for the Zephyr changes.
|
||||||
|
|
||||||
|
- Updated to newer devicetree/driver Zephyr API
|
||||||
|
- Adjustment for Zephyr pinmux changes
|
||||||
|
- Fixes for power management, LVGL, and formatter changes
|
||||||
|
|
||||||
|
## Getting The Changes
|
||||||
|
|
||||||
|
Use the following steps to update to the latest tooling in order to properly use the new ZMK changes:
|
||||||
|
|
||||||
|
### User Config Repositories Using GitHub Actions
|
||||||
|
|
||||||
|
Existing user config repositories using Github Actions to build will pull down Zephyr 2.5 automatically,
|
||||||
|
and should work, fine as is. However, to upgrade to the newer Docker image, you should:
|
||||||
|
|
||||||
|
- Open `.github/workflows/build.yml` in your editor/IDE
|
||||||
|
- Change `zmkfirmware/zmk-build-arm:2.4` to `zmkfirmware/zmk-build-arm:2.5` wherever it is found
|
||||||
|
|
||||||
|
:::note
|
||||||
|
|
||||||
|
If you created your user config repository a while ago, you may find that your `build.yml` file instead references
|
||||||
|
a `zephyr-west-action-arm` custom GitHub Action instead. In this case, the upgrade is not as direct. We suggest that
|
||||||
|
instead you [re-create your config repository](/docs/user-setup) to get an updated setup using the new automation
|
||||||
|
approach.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
### VS Code & Docker (Dev Container)
|
||||||
|
|
||||||
|
If you build locally using VS Code & Docker then:
|
||||||
|
|
||||||
|
- pull the latest ZMK `main` with `git pull` for your ZMK checkout
|
||||||
|
- reload the project
|
||||||
|
- if you are prompted to rebuild the remote container, click `Rebuild`
|
||||||
|
- otherwise, press `F1` and run `Remote Containers: Rebuild Container`
|
||||||
|
- Once the container has rebuilt and reloaded, run `west update` to pull the updated Zephyr version and its dependencies.
|
||||||
|
|
||||||
|
Once the container has rebuilt, VS Code will be running the 2.5 Docker image.
|
||||||
|
|
||||||
|
### Local Host Development
|
||||||
|
|
||||||
|
The following steps will get you building ZMK locally against Zephyr 2.5:
|
||||||
|
|
||||||
|
- Run the updated [toolchain installation](/docs/development/setup#toolchain-installation) steps, and once completed, remove the previously installed SDK version (optional, existing SDK should still work)
|
||||||
|
- pull the latest ZMK `main` with `git pull` for your ZMK checkout
|
||||||
|
- run `west update` to pull the updated Zephyr version and its dependencies
|
||||||
|
|
||||||
|
From there, you should be ready to build as normal!
|
||||||
|
|
||||||
|
## Thanks!
|
||||||
|
|
||||||
|
Thanks again to [innovaker] for all the hard work, and to all the testers who have helped verify ZMK functionality on the newer Zephyr version.
|
||||||
|
|
||||||
|
[petejohanson]: https://github.com/petejohanson
|
||||||
|
[innovaker]: https://github.com/innovaker
|
|
@ -84,5 +84,5 @@ ZMK support bluetooth “profiles” which allows connection to multiple devices
|
||||||
The bluetooth MAC address and negotiated keys during pairing are stored in the permanent storage on your chip and can be reused even after reflashing the firmware. If for some reason you want to delete the stored information, you can bind the `BT_CLR` behavior described above to a key and use it to clear the _current_ profile.
|
The bluetooth MAC address and negotiated keys during pairing are stored in the permanent storage on your chip and can be reused even after reflashing the firmware. If for some reason you want to delete the stored information, you can bind the `BT_CLR` behavior described above to a key and use it to clear the _current_ profile.
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
If you clear bond of a paired profile, make sure you do the same thing on the peer device as well (typically achieved by _removing_ or _forgetting_ the bluetooth connection). Otherwise the peer will try to connect to your keyboard whenever it discovers it. But while the MAC address of both devices could remain the same, the security key no longer match: the peer device still possess the old key negotiated in the previous pairing procedure, but our keyboard firmware has deleted that key. So the connection will fail. If you [enabled USB logging](../development/usb-logging), you might see a lot of failed connection attempts due to the reason of “Security failed”.
|
If you clear bond of a paired profile, make sure you do the same thing on the peer device as well (typically achieved by _removing_ or _forgetting_ the bluetooth connection). Otherwise the peer will try to connect to your keyboard whenever it discovers it. But while the MAC address of both devices could remain the same, the security key no longer match: the peer device still possess the old key negotiated in the previous pairing procedure, but our keyboard firmware has deleted that key. So the connection will fail. If you [enabled USB logging](../development/usb-logging.md), you might see a lot of failed connection attempts due to the reason of “Security failed”.
|
||||||
:::
|
:::
|
||||||
|
|
|
@ -33,7 +33,7 @@ When the hold-tap key is released and the hold behavior has not been triggered,
|
||||||
|
|
||||||
### Basic usage
|
### Basic usage
|
||||||
|
|
||||||
For basic usage, please see [mod-tap](./mod-tap.md) and [layer-tap](./layers.md) pages.
|
For basic usage, please see [mod-tap](mod-tap.md) and [layer-tap](layers.md) pages.
|
||||||
|
|
||||||
### Advanced Configuration
|
### Advanced Configuration
|
||||||
|
|
||||||
|
|
|
@ -10,14 +10,14 @@ a certain key.
|
||||||
|
|
||||||
The categories of supported codes are:
|
The categories of supported codes are:
|
||||||
|
|
||||||
- [Keyboard & Keypad](../codes/keyboard-keypad)
|
- [Keyboard & Keypad](../codes/keyboard-keypad.mdx)
|
||||||
- [Editing](../codes/editing)
|
- [Editing](../codes/editing.mdx)
|
||||||
- [Media](../codes/media)
|
- [Media](../codes/media.mdx)
|
||||||
- [Applications](../codes/applications)
|
- [Applications](../codes/applications.mdx)
|
||||||
- [Input Assist](../codes/input-assist)
|
- [Input Assist](../codes/input-assist.mdx)
|
||||||
- [Power](../codes/power)
|
- [Power](../codes/power.mdx)
|
||||||
|
|
||||||
Please visit the [codes](../codes) section for a comprehensive list.
|
Please visit the [codes](../codes/index.mdx) section for a comprehensive list.
|
||||||
|
|
||||||
For advanced users, user-defined HID usages are also supported but must be encoded, please see [`dt-bindings/zmk/keys.h`](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/keys.h) for further insight.
|
For advanced users, user-defined HID usages are also supported but must be encoded, please see [`dt-bindings/zmk/keys.h`](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/keys.h) for further insight.
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ Doing so makes a set of defines such as `A`, `N1`, etc. available for use with t
|
||||||
### Improperly defined keymap - `dtlib.DTError: <board>.dts.pre.tmp:<line number>`
|
### Improperly defined keymap - `dtlib.DTError: <board>.dts.pre.tmp:<line number>`
|
||||||
|
|
||||||
When compiling firmware from a keymap, it may be common to encounter an error in the form of a`dtlib.DTError: <board>.dts.pre.tmp:<line number>`.
|
When compiling firmware from a keymap, it may be common to encounter an error in the form of a`dtlib.DTError: <board>.dts.pre.tmp:<line number>`.
|
||||||
For instructions to resolve such an error, click [here](../troubleshooting###Improperly-defined-keymap)
|
For instructions to resolve such an error, click [here](../troubleshooting.md###Improperly-defined-keymap)
|
||||||
|
|
||||||
## Key Press
|
## Key Press
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ Example:
|
||||||
|
|
||||||
## Layer-tap
|
## Layer-tap
|
||||||
|
|
||||||
The "layer-tap" behavior enables a layer when a key is held, and output another key when the key is only tapped for a short time. For more information on the inner workings of layer-tap, see [hold-tap](./hold-tap.md).
|
The "layer-tap" behavior enables a layer when a key is held, and output another key when the key is only tapped for a short time. For more information on the inner workings of layer-tap, see [hold-tap](hold-tap.md).
|
||||||
|
|
||||||
### Behavior Binding
|
### Behavior Binding
|
||||||
|
|
||||||
|
|
|
@ -46,4 +46,4 @@ You can configure a different tapping term in your keymap:
|
||||||
|
|
||||||
### Additional information
|
### Additional information
|
||||||
|
|
||||||
The mod-tap is a [hold-tap](./hold-tap.md) under the hood with the "balanced" flavor and tapping-term-ms 200.
|
The mod-tap is a [hold-tap](hold-tap.md) under the hood with the "balanced" flavor and tapping-term-ms 200.
|
||||||
|
|
|
@ -24,7 +24,7 @@ import Table from "@site/src/components/codes/Table";
|
||||||
|
|
||||||
### Modifiers
|
### Modifiers
|
||||||
|
|
||||||
The [Modifiers](./modifiers) page includes further information.
|
The [Modifiers](modifiers.mdx) page includes further information.
|
||||||
|
|
||||||
<Table group="keyboard-modifiers" />
|
<Table group="keyboard-modifiers" />
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ sidebar_label: Customizing ZMK
|
||||||
---
|
---
|
||||||
|
|
||||||
After verifying you can successfully flash the default firmware, you will probably want to begin customizing your keymap and other keyboard options.
|
After verifying you can successfully flash the default firmware, you will probably want to begin customizing your keymap and other keyboard options.
|
||||||
[In the initial setup tutorial](user-setup), you created a Github repository called `zmk-config`. This repository is a discrete filesystem which works
|
[In the initial setup tutorial](user-setup.md), you created a Github repository called `zmk-config`. This repository is a discrete filesystem which works
|
||||||
with the main `zmk` firmware repository to build your desired firmware. The main advantage of a discrete configuration folder is ensuring that the
|
with the main `zmk` firmware repository to build your desired firmware. The main advantage of a discrete configuration folder is ensuring that the
|
||||||
working components of ZMK are kept separate from your personal keyboard settings, reducing the amount of file manipulation in the configuration process.
|
working components of ZMK are kept separate from your personal keyboard settings, reducing the amount of file manipulation in the configuration process.
|
||||||
This makes flashing ZMK to your keyboard much easier, especially because you don't need to keep an up-to-date copy of zmk on your computer at all times.
|
This makes flashing ZMK to your keyboard much easier, especially because you don't need to keep an up-to-date copy of zmk on your computer at all times.
|
||||||
|
@ -26,7 +26,7 @@ various config settings that can be commented/uncommented to modify how your fir
|
||||||
## Keymap
|
## Keymap
|
||||||
|
|
||||||
Once you have the basic user config completed, you can find the keymap file in `config/<shield>.keymap` and customize from there.
|
Once you have the basic user config completed, you can find the keymap file in `config/<shield>.keymap` and customize from there.
|
||||||
Refer to the [Keymap](/docs/features/keymaps) documentation to learn more.
|
Refer to the [Keymap](features/keymaps.md) documentation to learn more.
|
||||||
|
|
||||||
## Publishing
|
## Publishing
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ If you need to, a review of [Learn The Basics Of Git In Under 10 Minutes](https:
|
||||||
|
|
||||||
## Building from a local `zmk` fork using `zmk-config`
|
## Building from a local `zmk` fork using `zmk-config`
|
||||||
|
|
||||||
[As outlined here](development/build-flash), firmware comes in the form of .uf2 files, which can be built locally using the command `west build`. Normally,
|
[As outlined here](development/build-flash.md), firmware comes in the form of .uf2 files, which can be built locally using the command `west build`. Normally,
|
||||||
`west build` will default to using the in-tree .keymap and .conf files found in your local copy of the `zmk` repository. However, you can append the command, `-DZMK_CONFIG="C:/the/absolute/path/config"` to `west build` in order to use the contents of your `zmk-config` folder instead of the
|
`west build` will default to using the in-tree .keymap and .conf files found in your local copy of the `zmk` repository. However, you can append the command, `-DZMK_CONFIG="C:/the/absolute/path/config"` to `west build` in order to use the contents of your `zmk-config` folder instead of the
|
||||||
default keyboard settings.
|
default keyboard settings.
|
||||||
**Notice that this path should point to the folder labelled `config` within your `zmk-config` folder.**
|
**Notice that this path should point to the folder labelled `config` within your `zmk-config` folder.**
|
||||||
|
@ -55,4 +55,4 @@ west build -b nice_nano -- -DSHIELD=kyria_left -DZMK_CONFIG="C:/Users/myUser/Doc
|
||||||
For normal keyboards, follow the same flashing instructions as before to flash your updated firmware.
|
For normal keyboards, follow the same flashing instructions as before to flash your updated firmware.
|
||||||
|
|
||||||
For split keyboards, only the central (left) side will need to be reflashed if you are just updating your keymap.
|
For split keyboards, only the central (left) side will need to be reflashed if you are just updating your keymap.
|
||||||
More troubleshooting information for split keyboards can be found [here](troubleshooting#split-keyboard-halves-unable-to-pair).
|
More troubleshooting information for split keyboards can be found [here](troubleshooting.md#split-keyboard-halves-unable-to-pair).
|
||||||
|
|
|
@ -6,12 +6,12 @@ title: Boards, Shields, and Keymaps
|
||||||
|
|
||||||
The foundational elements needed to get a specific keyboard working with ZMK can be broken down into:
|
The foundational elements needed to get a specific keyboard working with ZMK can be broken down into:
|
||||||
|
|
||||||
- A [KSCAN driver](https://docs.zephyrproject.org/2.3.0/reference/peripherals/kscan.html), which uses `compatible="zmk,kscan-gpio-matrix"` for GPIO matrix based keyboards, or uses `compatible="zmk,kscan-gpio-direct"` for small direct wires.
|
- A [KSCAN driver](https://docs.zephyrproject.org/2.5.0/reference/peripherals/kscan.html), which uses `compatible="zmk,kscan-gpio-matrix"` for GPIO matrix based keyboards, or uses `compatible="zmk,kscan-gpio-direct"` for small direct wires.
|
||||||
- An optional matrix transform, which defines how the KSCAN row/column events are translated into logical "key positions". This is required for non-rectangular keyboards/matrices, where the key positions don't naturally follow the row/columns from the GPIO matrix.
|
- An optional matrix transform, which defines how the KSCAN row/column events are translated into logical "key positions". This is required for non-rectangular keyboards/matrices, where the key positions don't naturally follow the row/columns from the GPIO matrix.
|
||||||
- A keymap, which binds each key position to a behavior, e.g. key press, mod-tap, momentary layer, in a set of layers.
|
- A keymap, which binds each key position to a behavior, e.g. key press, mod-tap, momentary layer, in a set of layers.
|
||||||
|
|
||||||
These three core architectural elements are defined per-keyboard, and _where_ they are defined depends on the specifics of how that
|
These three core architectural elements are defined per-keyboard, and _where_ they are defined depends on the specifics of how that
|
||||||
keyboard works. For an overview on the general concepts of boards and shields, please see the [FAQs on boards and shields](/docs/faq#why-boards-and-shields--why-not-just-keyboard).
|
keyboard works. For an overview on the general concepts of boards and shields, please see the [FAQs on boards and shields](../faq.md#why-boards-and-shields--why-not-just-keyboard).
|
||||||
|
|
||||||
## Self-Contained Keyboard
|
## Self-Contained Keyboard
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ in the `app/boards/${arch}/${board_name}` directory, e.g. `app/boards/arm/planck
|
||||||
- A `${board_name}_defconfig` file that forces specific Kconfig settings that are specific to this hardware configuration. Mostly this is SoC settings around the specific hardware configuration.
|
- A `${board_name}_defconfig` file that forces specific Kconfig settings that are specific to this hardware configuration. Mostly this is SoC settings around the specific hardware configuration.
|
||||||
- `${board_name}.dts` which contains all the devicetree definitions, including:
|
- `${board_name}.dts` which contains all the devicetree definitions, including:
|
||||||
- An `#include` line that pulls in the specific microprocessor that is used, e.g. `#include <st/f3/stm32f303Xc.dtsi>`.
|
- An `#include` line that pulls in the specific microprocessor that is used, e.g. `#include <st/f3/stm32f303Xc.dtsi>`.
|
||||||
- A [chosen](https://docs.zephyrproject.org/2.3.0/guides/dts/intro.html#aliases-and-chosen-nodes) node named `zmk,kscan` which references the configured KSCAN driver (usually a GPIO matrix)
|
- A [chosen](https://docs.zephyrproject.org/2.5.0/guides/dts/intro.html#aliases-and-chosen-nodes) node named `zmk,kscan` which references the configured KSCAN driver (usually a GPIO matrix)
|
||||||
- (Optional) A [chosen](https://docs.zephyrproject.org/2.3.0/guides/dts/intro.html#aliases-and-chosen-nodes) node named `zmk,matrix_transform` that defines the mapping from KSCAN row/column values to the logical key position for the keyboard.
|
- (Optional) A [chosen](https://docs.zephyrproject.org/2.5.0/guides/dts/intro.html#aliases-and-chosen-nodes) node named `zmk,matrix_transform` that defines the mapping from KSCAN row/column values to the logical key position for the keyboard.
|
||||||
- A `board.cmake` file with CMake directives for how to flash to the device.
|
- A `board.cmake` file with CMake directives for how to flash to the device.
|
||||||
- A `keymap/keymap.overlay` file that includes the default keymap for that keyboard. Users will be able to override this keymap in their user configs.
|
- A `keymap/keymap.overlay` file that includes the default keymap for that keyboard. Users will be able to override this keymap in their user configs.
|
||||||
|
|
||||||
|
@ -47,6 +47,6 @@ in the `app/boards/shields/${board_name}` directory, e.g. `app/boards/shields/cl
|
||||||
- A `Kconfig.shield` that defines the toplevel Kconfig value for the shield, which uses a supplied utility to function to default the value based on the shield list, e.g. `def_bool $(shields_list_contains,clueboard_california)`.
|
- A `Kconfig.shield` that defines the toplevel Kconfig value for the shield, which uses a supplied utility to function to default the value based on the shield list, e.g. `def_bool $(shields_list_contains,clueboard_california)`.
|
||||||
- A `Kconfig.defconfig` file to set default values for things like `ZMK_KEYBOARD_NAME`
|
- A `Kconfig.defconfig` file to set default values for things like `ZMK_KEYBOARD_NAME`
|
||||||
- A `${shield_name}.overlay` file, which is a devicetree overlay file, that includes:
|
- A `${shield_name}.overlay` file, which is a devicetree overlay file, that includes:
|
||||||
- A [chosen](https://docs.zephyrproject.org/2.3.0/guides/dts/intro.html#aliases-and-chosen-nodes) node named `zmk,kscan` which references the configured KSCAN driver (usually a GPIO matrix). For these keyboards, to be compatible with any Pro Micro compatible boards, the KSCAN configuration should reference the [nexus node](https://docs.zephyrproject.org/2.3.0/guides/porting/shields.html#gpio-nexus-nodes) that ZMK has standardized on. In particular, the `&pro_micro_a` and `&pro_micro_d` aliases can be used to reference the standard `A#` and `D#` pins in shields.
|
- A [chosen](https://docs.zephyrproject.org/2.5.0/guides/dts/intro.html#aliases-and-chosen-nodes) node named `zmk,kscan` which references the configured KSCAN driver (usually a GPIO matrix). For these keyboards, to be compatible with any Pro Micro compatible boards, the KSCAN configuration should reference the [nexus node](https://docs.zephyrproject.org/2.5.0/guides/porting/shields.html#gpio-nexus-nodes) that ZMK has standardized on. In particular, the `&pro_micro_a` and `&pro_micro_d` aliases can be used to reference the standard `A#` and `D#` pins in shields.
|
||||||
- (Optional) A [chosen](https://docs.zephyrproject.org/2.3.0/guides/dts/intro.html#aliases-and-chosen-nodes) node named `zmk,matrix_transform` that defines the mapping from KSCAN row/column values to the logical key position for the keyboard.
|
- (Optional) A [chosen](https://docs.zephyrproject.org/2.5.0/guides/dts/intro.html#aliases-and-chosen-nodes) node named `zmk,matrix_transform` that defines the mapping from KSCAN row/column values to the logical key position for the keyboard.
|
||||||
- A `keymap/keymap.overlay` file that includes the default keymap for that keyboard. Users will be able to override this keymap in their user configs.
|
- A `keymap/keymap.overlay` file that includes the default keymap for that keyboard. Users will be able to override this keymap in their user configs.
|
||||||
|
|
|
@ -31,7 +31,7 @@ an onboard MCU, or one that uses an MCU board addon.
|
||||||
|
|
||||||
### Keyboard (Shield) + MCU Board
|
### Keyboard (Shield) + MCU Board
|
||||||
|
|
||||||
ZMK treats keyboards that take an MCU addon board as [shields](https://docs.zephyrproject.org/2.3.0/guides/porting/shields.html), and treats the smaller MCU board as the true [board](https://docs.zephyrproject.org/2.3.0/guides/porting/board_porting.html)
|
ZMK treats keyboards that take an MCU addon board as [shields](https://docs.zephyrproject.org/2.5.0/guides/porting/shields.html), and treats the smaller MCU board as the true [board](https://docs.zephyrproject.org/2.5.0/guides/porting/board_porting.html)
|
||||||
|
|
||||||
Given the following:
|
Given the following:
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ west build -b proton_c -- -DSHIELD=kyria_left
|
||||||
|
|
||||||
### Keyboard With Onboard MCU
|
### Keyboard With Onboard MCU
|
||||||
|
|
||||||
Keyboards with onboard MCU chips are simply treated as the [board](https://docs.zephyrproject.org/2.3.0/guides/porting/board_porting.html) as far as Zephyr™ is concerned.
|
Keyboards with onboard MCU chips are simply treated as the [board](https://docs.zephyrproject.org/2.5.0/guides/porting/board_porting.html) as far as Zephyr™ is concerned.
|
||||||
|
|
||||||
Given the following:
|
Given the following:
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ This produces `left` and `right` subfolders under the `build` directory and two
|
||||||
|
|
||||||
### Building from `zmk-config` Folder
|
### Building from `zmk-config` Folder
|
||||||
|
|
||||||
Instead of building .uf2 files using the default keymap and config files, you can build directly from your [`zmk-config` folder](../user-setup#github-repo) by adding
|
Instead of building .uf2 files using the default keymap and config files, you can build directly from your [`zmk-config` folder](../user-setup.md#github-repo) by adding
|
||||||
`-DZMK_CONFIG="C:/the/absolute/path/config"` to your `west build` command. **Notice that this path should point to the folder labelled `config` within your `zmk-config` folder.**
|
`-DZMK_CONFIG="C:/the/absolute/path/config"` to your `west build` command. **Notice that this path should point to the folder labelled `config` within your `zmk-config` folder.**
|
||||||
|
|
||||||
For instance, building kyria firmware from a user `myUser`'s `zmk-config` folder on Windows 10 may look something like this:
|
For instance, building kyria firmware from a user `myUser`'s `zmk-config` folder on Windows 10 may look something like this:
|
||||||
|
@ -107,7 +107,7 @@ volume automatically -- we need to delete the default volume before binding it t
|
||||||
1. Remove all the containers that are not running via the command `docker container prune`. We need to remove the ZMK container before we can delete the default `zmk-config` volume referenced by it. If you do not want to delete all the containers that are not running, you can find the id of the ZMK container and use `docker rm` to delete that one only.
|
1. Remove all the containers that are not running via the command `docker container prune`. We need to remove the ZMK container before we can delete the default `zmk-config` volume referenced by it. If you do not want to delete all the containers that are not running, you can find the id of the ZMK container and use `docker rm` to delete that one only.
|
||||||
1. Remove the default volume via the command `docker volume rm zmk-config`.
|
1. Remove the default volume via the command `docker volume rm zmk-config`.
|
||||||
|
|
||||||
Then you can bind the `zmk-config` volume to the correct path pointing to your local [zmk-config](./customization.md) folder:
|
Then you can bind the `zmk-config` volume to the correct path pointing to your local [zmk-config](customization.md) folder:
|
||||||
|
|
||||||
```
|
```
|
||||||
docker volume create --driver local -o o=bind -o type=none -o \
|
docker volume create --driver local -o o=bind -o type=none -o \
|
||||||
|
|
|
@ -35,7 +35,7 @@ terminal to the ZMK repository and run the following command:
|
||||||
west config build.cmake-args -- -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
west config build.cmake-args -- -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||||||
```
|
```
|
||||||
|
|
||||||
Every [build](build-flash#building) will now update the database. You will
|
Every [build](build-flash.md#building) will now update the database. You will
|
||||||
need to build once to create the database before code completion will work.
|
need to build once to create the database before code completion will work.
|
||||||
We'll tell Visual Studio Code where to find the database in the next step.
|
We'll tell Visual Studio Code where to find the database in the next step.
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ The high level steps are:
|
||||||
- Add support for features such as encoders, OLED displays, or RGB underglow.
|
- Add support for features such as encoders, OLED displays, or RGB underglow.
|
||||||
- Update build.yml
|
- Update build.yml
|
||||||
|
|
||||||
It may be helpful to review the upstream [shields documentation](https://docs.zephyrproject.org/2.3.0/guides/porting/shields.html#shields) to get a proper understanding of the underlying system before continuing.
|
It may be helpful to review the upstream [shields documentation](https://docs.zephyrproject.org/2.5.0/guides/porting/shields.html#shields) to get a proper understanding of the underlying system before continuing.
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
ZMK support for split keyboards requires a few more files than single boards to ensure proper connectivity between the central and peripheral units. Check the following guides thoroughly to ensure that all the files are in place.
|
ZMK support for split keyboards requires a few more files than single boards to ensure proper connectivity between the central and peripheral units. Check the following guides thoroughly to ensure that all the files are in place.
|
||||||
|
@ -30,7 +30,7 @@ ZMK support for split keyboards requires a few more files than single boards to
|
||||||
:::note
|
:::note
|
||||||
This guide describes how to add shield to the ZMK main repository. If you are building firmware for your
|
This guide describes how to add shield to the ZMK main repository. If you are building firmware for your
|
||||||
own prototype or handwired keyboard, it is recommended to use your own user config repository. Follow the
|
own prototype or handwired keyboard, it is recommended to use your own user config repository. Follow the
|
||||||
[user setup guide](./user-setup.md) to create your user config repository first. When following the rest
|
[user setup guide](user-setup.md) to create your user config repository first. When following the rest
|
||||||
of this guide, replace the `app/` directory in the ZMK main repository with the `config/` directory in your
|
of this guide, replace the `app/` directory in the ZMK main repository with the `config/` directory in your
|
||||||
user config repository. For example, `app/boards/shields/<keyboard_name>` should now be
|
user config repository. For example, `app/boards/shields/<keyboard_name>` should now be
|
||||||
`config/boards/shields/<keyboard_name>`.
|
`config/boards/shields/<keyboard_name>`.
|
||||||
|
@ -449,7 +449,7 @@ Add the following line to your keymap file to add default encoder behavior bindi
|
||||||
sensor-bindings = <&inc_dec_kp C_VOL_UP C_VOL_DN>;
|
sensor-bindings = <&inc_dec_kp C_VOL_UP C_VOL_DN>;
|
||||||
```
|
```
|
||||||
|
|
||||||
Add additional bindings as necessary to match the default number of encoders on your board. See the [Encoders](/docs/features/encoders) and [Keymap](/docs/features/keymaps) feature documentation for more details.
|
Add additional bindings as necessary to match the default number of encoders on your board. See the [Encoders](../features/encoders.md) and [Keymap](../features/keymaps.md) feature documentation for more details.
|
||||||
|
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
@ -477,11 +477,11 @@ west flash
|
||||||
```
|
```
|
||||||
|
|
||||||
Please have a look at documentation specific to
|
Please have a look at documentation specific to
|
||||||
[building and flashing](build-flash) for additional information.
|
[building and flashing](build-flash.md) for additional information.
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
Further testing your keyboard shield without altering the root keymap file can be done with the use of `-DZMK_CONFIG` in your `west build` command,
|
Further testing your keyboard shield without altering the root keymap file can be done with the use of `-DZMK_CONFIG` in your `west build` command,
|
||||||
shown [here](build-flash#building-from-zmk-config-folder)
|
shown [here](build-flash.md#building-from-zmk-config-folder)
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## Updating `build.yml`
|
## Updating `build.yml`
|
||||||
|
|
|
@ -53,7 +53,7 @@ sudo apt install -y \
|
||||||
autoconf \
|
autoconf \
|
||||||
automake \
|
automake \
|
||||||
build-essential \
|
build-essential \
|
||||||
bzip2 \
|
bzip2 \
|
||||||
ccache \
|
ccache \
|
||||||
device-tree-compiler \
|
device-tree-compiler \
|
||||||
dfu-util \
|
dfu-util \
|
||||||
|
@ -95,7 +95,7 @@ sudo apt install -y \
|
||||||
autoconf \
|
autoconf \
|
||||||
automake \
|
automake \
|
||||||
build-essential \
|
build-essential \
|
||||||
bzip2 \
|
bzip2 \
|
||||||
ccache \
|
ccache \
|
||||||
device-tree-compiler \
|
device-tree-compiler \
|
||||||
dfu-util \
|
dfu-util \
|
||||||
|
@ -135,7 +135,7 @@ sudo dnf install -y \
|
||||||
wget \
|
wget \
|
||||||
autoconf \
|
autoconf \
|
||||||
automake \
|
automake \
|
||||||
bzip2 \
|
bzip2 \
|
||||||
ccache \
|
ccache \
|
||||||
dtc \
|
dtc \
|
||||||
dfu-util \
|
dfu-util \
|
||||||
|
@ -188,7 +188,7 @@ brew install cmake ninja python3 ccache dtc git wget dfu-util
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem value="docker">
|
<TabItem value="docker">
|
||||||
|
|
||||||
This setup leverages the same [image which is used by the GitHub action](https://github.com/zmkfirmware/zephyr-west-action) for local development. Beyond the benefits of [dev/prod parity](https://12factor.net/dev-prod-parity), this approach is also the easiest to set up. No toolchain or dependencies are necessary when using Docker; the container image you'll be using already has the toolchain installed and set up to use.
|
This setup leverages the same [image which is used by the GitHub action](https://github.com/zmkfirmware/zmk-docker) for local development. Beyond the benefits of [dev/prod parity](https://12factor.net/dev-prod-parity), this approach is also the easiest to set up. No toolchain or dependencies are necessary when using Docker; the container image you'll be using already has the toolchain installed and set up to use.
|
||||||
|
|
||||||
1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop) for your operating system.
|
1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop) for your operating system.
|
||||||
2. Install [VS Code](https://code.visualstudio.com/)
|
2. Install [VS Code](https://code.visualstudio.com/)
|
||||||
|
@ -205,9 +205,9 @@ The docker container includes `west` and the compilation toolchain. If you're us
|
||||||
|
|
||||||
### West Installation
|
### West Installation
|
||||||
|
|
||||||
`west` is the [Zephyr™ meta-tool](https://docs.zephyrproject.org/2.3.0/guides/west/index.html) used to configure and build Zephyr™ applications.
|
`west` is the [Zephyr™ meta-tool](https://docs.zephyrproject.org/2.5.0/guides/west/index.html) used to configure and build Zephyr™ applications.
|
||||||
|
|
||||||
West can be installed by using the `pip` python package manager. The [Zephyr™ instructions](https://docs.zephyrproject.org/latest/guides/west/install.html#installing-west) are summarized here:
|
West can be installed by using the `pip` python package manager. The [Zephyr™ instructions](https://docs.zephyrproject.org/2.5.0/guides/west/install.html#installing-west) are summarized here:
|
||||||
|
|
||||||
<Tabs
|
<Tabs
|
||||||
defaultValue="linux"
|
defaultValue="linux"
|
||||||
|
@ -280,10 +280,10 @@ platform.
|
||||||
To build firmwares for the ARM architecture (all supported MCUs/keyboards at this point), you'll need to install the Zephyr™ ARM SDK to your system:
|
To build firmwares for the ARM architecture (all supported MCUs/keyboards at this point), you'll need to install the Zephyr™ ARM SDK to your system:
|
||||||
|
|
||||||
```
|
```
|
||||||
export ZSDK_VERSION=0.11.4
|
export ZSDK_VERSION=0.12.4
|
||||||
wget -q "https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v${ZSDK_VERSION}/zephyr-toolchain-arm-${ZSDK_VERSION}-setup.run" && \
|
wget -q "https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v${ZSDK_VERSION}/zephyr-toolchain-arm-${ZSDK_VERSION}-x86_64-linux-setup.run" && \
|
||||||
sh "zephyr-toolchain-arm-${ZSDK_VERSION}-setup.run" --quiet -- -d ~/.local/zephyr-sdk-${ZSDK_VERSION} && \
|
sh "zephyr-toolchain-arm-${ZSDK_VERSION}-x86_64-linux-setup.run" --quiet -- -d ~/.local/zephyr-sdk-${ZSDK_VERSION} && \
|
||||||
rm "zephyr-toolchain-arm-${ZSDK_VERSION}-setup.run"
|
rm "zephyr-toolchain-arm-${ZSDK_VERSION}-x86_64-linux-setup.run"
|
||||||
```
|
```
|
||||||
|
|
||||||
The installation will prompt with several questions about installation location, and creating a default `~/.zephyrrc` for you with various variables. The defaults should normally work as expected.
|
The installation will prompt with several questions about installation location, and creating a default `~/.zephyrrc` for you with various variables. The defaults should normally work as expected.
|
||||||
|
@ -292,7 +292,7 @@ The installation will prompt with several questions about installation location,
|
||||||
<TabItem value="raspberryos">
|
<TabItem value="raspberryos">
|
||||||
|
|
||||||
Because Raspberry OS (Raspbian) runs on the same architecture (but different ABI) as the keyboard MCUs,
|
Because Raspberry OS (Raspbian) runs on the same architecture (but different ABI) as the keyboard MCUs,
|
||||||
the operating system's installed [cross compilers](https://docs.zephyrproject.org/2.3.0/getting_started/toolchain_other_x_compilers.html) can be used to target the different ABI.
|
the operating system's installed [cross compilers](https://docs.zephyrproject.org/2.5.0/getting_started/toolchain_other_x_compilers.html) can be used to target the different ABI.
|
||||||
|
|
||||||
First, the cross compiler should be installed:
|
First, the cross compiler should be installed:
|
||||||
|
|
||||||
|
@ -315,10 +315,10 @@ export CROSS_COMPILE=/usr/bin/arm-none-eabi-
|
||||||
To build firmwares for the ARM architecture (all supported MCUs/keyboards at this point), you'll need to install the Zephyr™ ARM SDK to your system:
|
To build firmwares for the ARM architecture (all supported MCUs/keyboards at this point), you'll need to install the Zephyr™ ARM SDK to your system:
|
||||||
|
|
||||||
```
|
```
|
||||||
export ZSDK_VERSION=0.11.4
|
export ZSDK_VERSION=0.12.4
|
||||||
wget -q "https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v${ZSDK_VERSION}/zephyr-toolchain-arm-${ZSDK_VERSION}-setup.run" && \
|
wget -q "https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v${ZSDK_VERSION}/zephyr-toolchain-arm-${ZSDK_VERSION}-x86_64-linux-setup.run" && \
|
||||||
sh "zephyr-toolchain-arm-${ZSDK_VERSION}-setup.run" --quiet -- -d ~/.local/zephyr-sdk-${ZSDK_VERSION} && \
|
sh "zephyr-toolchain-arm-${ZSDK_VERSION}-x86_64-linux-setup.run" --quiet -- -d ~/.local/zephyr-sdk-${ZSDK_VERSION} && \
|
||||||
rm "zephyr-toolchain-arm-\${ZSDK_VERSION}-setup.run"
|
rm "zephyr-toolchain-arm-${ZSDK_VERSION}-x86_64-linux-setup.run"
|
||||||
```
|
```
|
||||||
|
|
||||||
The installation will prompt with several questions about installation location, and creating a default `~/.zephyrrc` for you with various variables. The defaults should normally work as expected.
|
The installation will prompt with several questions about installation location, and creating a default `~/.zephyrrc` for you with various variables. The defaults should normally work as expected.
|
||||||
|
@ -328,14 +328,14 @@ The installation will prompt with several questions about installation location,
|
||||||
|
|
||||||
#### GNU ARM Embedded
|
#### GNU ARM Embedded
|
||||||
|
|
||||||
Since the Zephyr™ SDK is not available for Windows, we recommending following the [Zephyr documentation](https://docs.zephyrproject.org/2.3.0/getting_started/toolchain_3rd_party_x_compilers.html#gnu-arm-embedded) to install a GNU ARM Embedded build. Note the warnings regarding installing the toolchain into a path with spaces, and make sure to follow the steps to add the environment variables which are also summarized with screenshots in the [Environment Variables](#environment-variables) section below.
|
Since the Zephyr™ SDK is not available for Windows, we recommending following the [Zephyr documentation](https://docs.zephyrproject.org/2.5.0/getting_started/toolchain_3rd_party_x_compilers.html#gnu-arm-embedded) to install a GNU ARM Embedded build. Note the warnings regarding installing the toolchain into a path with spaces, and make sure to follow the steps to add the environment variables which are also summarized with screenshots in the [Environment Variables](#environment-variables) section below.
|
||||||
|
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem value="mac">
|
<TabItem value="mac">
|
||||||
|
|
||||||
#### GNU ARM Embedded
|
#### GNU ARM Embedded
|
||||||
|
|
||||||
Since the Zephyr™ SDK is not available for macOS, we recommending following the steps to install the [GNU ARM Embedded](https://docs.zephyrproject.org/2.3.0/getting_started/toolchain_3rd_party_x_compilers.html#gnu-arm-embedded).
|
Since the Zephyr™ SDK is not available for macOS, we recommending following the steps to install the [GNU ARM Embedded](https://docs.zephyrproject.org/2.5.0/getting_started/toolchain_3rd_party_x_compilers.html#gnu-arm-embedded).
|
||||||
|
|
||||||
The install command is:
|
The install command is:
|
||||||
|
|
||||||
|
@ -345,7 +345,7 @@ brew install --cask gcc-arm-embedded
|
||||||
|
|
||||||
:::warning Security Controls Workaround
|
:::warning Security Controls Workaround
|
||||||
|
|
||||||
Please be sure to read the [additional setup instructions](https://docs.zephyrproject.org/2.3.0/getting_started/installation_mac.html#mac-gatekeeper) needed to address security controls found in macOS 10.15 Catalina and newer
|
Please be sure to read the [additional setup instructions](https://docs.zephyrproject.org/2.5.0/getting_started/installation_mac.html#mac-gatekeeper) needed to address security controls found in macOS 10.15 Catalina and newer
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
@ -456,7 +456,7 @@ This step pulls down quite a bit of tooling. Go grab a cup of coffee, it can tak
|
||||||
:::info
|
:::info
|
||||||
If you're using Docker, you're done with setup! You must restart the container at this point. The easiest way to do so is to close the VS Code window, verify that the container has stopped in Docker Dashboard, and reopen the container with VS Code.
|
If you're using Docker, you're done with setup! You must restart the container at this point. The easiest way to do so is to close the VS Code window, verify that the container has stopped in Docker Dashboard, and reopen the container with VS Code.
|
||||||
|
|
||||||
Once your container is restarted, proceed to [Building and Flashing](./development/build-flash.md).
|
Once your container is restarted, proceed to [Building and Flashing](development/build-flash.md).
|
||||||
:::
|
:::
|
||||||
|
|
||||||
#### Export Zephyr™ Core
|
#### Export Zephyr™ Core
|
||||||
|
@ -514,7 +514,7 @@ On Windows, only two environment variables need to be set for ZMK to build prope
|
||||||
#### For Zephyr
|
#### For Zephyr
|
||||||
|
|
||||||
By default, the Zephyr™ SDK will create a file named `~/.zephyrrc` with the correct environment variables to build ZMK.
|
By default, the Zephyr™ SDK will create a file named `~/.zephyrrc` with the correct environment variables to build ZMK.
|
||||||
We suggest two main [options](https://docs.zephyrproject.org/2.3.0/guides/env_vars.html?highlight=zephyrrc) for how to load those settings.
|
We suggest two main [options](https://docs.zephyrproject.org/2.5.0/guides/env_vars.html#option-3-using-zephyrrc-files) for how to load those settings.
|
||||||
|
|
||||||
##### Per Shell
|
##### Per Shell
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ title: Tests
|
||||||
sidebar_label: Tests
|
sidebar_label: Tests
|
||||||
---
|
---
|
||||||
|
|
||||||
Running tests requires [native posix support](posix-board). Any folder under `/app/tests`
|
Running tests requires [native posix support](posix-board.md). Any folder under `/app/tests`
|
||||||
containing `native_posix.keymap` will be selected when running `west test`.
|
containing `native_posix.keymap` will be selected when running `west test`.
|
||||||
|
|
||||||
Run a single test with `west test <testname>`, like `west test tests/toggle-layer/normal`.
|
Run a single test with `west test <testname>`, like `west test tests/toggle-layer/normal`.
|
||||||
|
|
|
@ -24,7 +24,7 @@ The `CONFIG_ZMK_USB_LOGGING` KConfig value needs to be set, either by copy and p
|
||||||
`west build -t menuconfig` and manually enabling the setting in that UI at `ZMK -> Advanced -> USB Logging`.
|
`west build -t menuconfig` and manually enabling the setting in that UI at `ZMK -> Advanced -> USB Logging`.
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
If you are debugging your own keyboard in your [user config repository](./user-setup.md), use
|
If you are debugging your own keyboard in your [user config repository](user-setup.md), use
|
||||||
`config/boards/shields/<your_keyboard>/<your_keyboard>.conf` instead of `app/prj.conf`. In Github
|
`config/boards/shields/<your_keyboard>/<your_keyboard>.conf` instead of `app/prj.conf`. In Github
|
||||||
Actions, you can search the `Kconfig file` build log to verify the options above have been enabled
|
Actions, you can search the `Kconfig file` build log to verify the options above have been enabled
|
||||||
for you successfully.
|
for you successfully.
|
||||||
|
|
|
@ -5,6 +5,10 @@ sidebar_label: Encoders
|
||||||
|
|
||||||
Existing support for encoders in ZMK is focused around the five pin EC11 rotary encoder with push button design used in the majority of current keyboard and macropad designs.
|
Existing support for encoders in ZMK is focused around the five pin EC11 rotary encoder with push button design used in the majority of current keyboard and macropad designs.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
Encoders are currently only support on the left/central sides of splits. For progress on this, see [#728](https://github.com/zmkfirmware/zmk/pull/728).
|
||||||
|
:::
|
||||||
|
|
||||||
## Enabling EC11 Encoders
|
## Enabling EC11 Encoders
|
||||||
|
|
||||||
To enable encoders for boards that have existing encoder support, uncomment the `EC11_CONFIG=y` and `CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y` lines in your board's .conf file in your `zmk-config/config` folder. Save and push your changes, then download and flash the new firmware.
|
To enable encoders for boards that have existing encoder support, uncomment the `EC11_CONFIG=y` and `CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y` lines in your board's .conf file in your `zmk-config/config` folder. Save and push your changes, then download and flash the new firmware.
|
||||||
|
@ -25,7 +29,7 @@ Rotation is handled separately as a type of sensor. The behavior for this is set
|
||||||
sensor-bindings = <BINDING CW_KEY CCW_KEY>;
|
sensor-bindings = <BINDING CW_KEY CCW_KEY>;
|
||||||
```
|
```
|
||||||
|
|
||||||
- `BINDING`, for now, has only one behavior available; `&inc_dec_kp` for key presses (see [Key Press](/docs/behaviors/key-press) for details on available keycodes).
|
- `BINDING`, for now, has only one behavior available; `&inc_dec_kp` for key presses (see [Key Press](../behaviors/key-press.md) for details on available keycodes).
|
||||||
- `CW_KEY` is the keycode activated by a clockwise turn.
|
- `CW_KEY` is the keycode activated by a clockwise turn.
|
||||||
- `CCW_KEY` is the keycode activated by a counter-clockwise turn.
|
- `CCW_KEY` is the keycode activated by a counter-clockwise turn.
|
||||||
|
|
||||||
|
@ -41,4 +45,4 @@ Here, the left encoder is configured to control volume up and down while the rig
|
||||||
|
|
||||||
## Adding Encoder Support
|
## Adding Encoder Support
|
||||||
|
|
||||||
See the [New Keyboard Shield](/docs/development/new-shield#encoders) documentation for how to add or modify additional encoders to your shield.
|
See the [New Keyboard Shield](../development/new-shield.md#encoders) documentation for how to add or modify additional encoders to your shield.
|
||||||
|
|
|
@ -33,7 +33,7 @@ For example, the simplest behavior in ZMK is the "key press" behavior, which res
|
||||||
(a certain spot on the keyboard), and when that position is pressed, send a keycode to the host, and
|
(a certain spot on the keyboard), and when that position is pressed, send a keycode to the host, and
|
||||||
when the key position is released, updates the host to notify of the keycode being released.
|
when the key position is released, updates the host to notify of the keycode being released.
|
||||||
|
|
||||||
For the full set of possible behaviors, start at the [Key Press](/docs/behaviors/key-press) behavior.
|
For the full set of possible behaviors, start at the [Key Press](../behaviors/key-press.md) behavior.
|
||||||
|
|
||||||
## Layers
|
## Layers
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ The second include brings in the defines for all the keycodes (e.g. `A`, `N1`, `
|
||||||
|
|
||||||
### Root devicetree Node
|
### Root devicetree Node
|
||||||
|
|
||||||
ALl the remaining keymap nodes will be nested inside of the root devicetree node, like so:
|
All the remaining keymap nodes will be nested inside of the root devicetree node, like so:
|
||||||
|
|
||||||
```devicetree
|
```devicetree
|
||||||
/ {
|
/ {
|
||||||
|
@ -131,7 +131,7 @@ Each layer should have:
|
||||||
1. A `bindings` property this will be a list of behaviour bindings, one for each key position for the keyboard.
|
1. A `bindings` property this will be a list of behaviour bindings, one for each key position for the keyboard.
|
||||||
1. (Optional) A `sensor-bindings` property that will be a list of behavior bindings for each sensor on the keyboard. (Currently, only encoders are supported as sensor hardware, but in the future devices like trackpoints would be supported the same way)
|
1. (Optional) A `sensor-bindings` property that will be a list of behavior bindings for each sensor on the keyboard. (Currently, only encoders are supported as sensor hardware, but in the future devices like trackpoints would be supported the same way)
|
||||||
|
|
||||||
For the full set of possible behaviors, start at the [Key Press](/docs/behaviors/key-press) behavior.
|
For the full set of possible behaviors, start at the [Key Press](../behaviors/key-press.md) behavior.
|
||||||
|
|
||||||
### Complete Example
|
### Complete Example
|
||||||
|
|
||||||
|
|
|
@ -11,11 +11,11 @@ have had their hardware details codified in boards/shields for ZMK.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
With the solid technical foundation of Zephyr™ RTOS, ZMK can support a wide diversity of hardware targets.
|
With the solid technical foundation of Zephyr™ RTOS, ZMK can support a wide diversity of hardware targets.
|
||||||
That being said, there are currently only a few specific [boards](/docs/faq#what-is-a-board)/[shields](/docs/faq#what-is-a-shield) that have been written and tested by the ZMK contributors.
|
That being said, there are currently only a few specific [boards](faq.md#what-is-a-board)/[shields](faq.md#what-is-a-shield) that have been written and tested by the ZMK contributors.
|
||||||
|
|
||||||
## Boards
|
## Boards
|
||||||
|
|
||||||
- [nice!nano](https://nicekeyboards.com/products/nice-nano-v1-0) (`nice_nano`)
|
- [nice!nano](https://nicekeyboards.com/nice-nano) (`nice_nano`, `nice_nano_v2`)
|
||||||
- [nrfMicro](https://github.com/joric/nrfmicro) (`nrfmicro_13`, `nrfmicro_11`, `nrfmicro_11_flipped`)
|
- [nrfMicro](https://github.com/joric/nrfmicro) (`nrfmicro_13`, `nrfmicro_11`, `nrfmicro_11_flipped`)
|
||||||
- [BlueMicro840](https://store.jpconstantineau.com/#/group/bluemicro) (`bluemicro840_v1`)
|
- [BlueMicro840](https://store.jpconstantineau.com/#/group/bluemicro) (`bluemicro840_v1`)
|
||||||
- [QMK Proton-C](https://qmk.fm/proton-c/) (`proton_c`)
|
- [QMK Proton-C](https://qmk.fm/proton-c/) (`proton_c`)
|
||||||
|
@ -53,4 +53,4 @@ Until detailed documentation is available, feel free to ask questions about how
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
If you'd like to add support for a new keyboard shield, head over to the [New Keyboard Shield](development/new-shield) documentation.
|
If you'd like to add support for a new keyboard shield, head over to the [New Keyboard Shield](development/new-shield.md) documentation.
|
||||||
|
|
|
@ -14,32 +14,32 @@ ZMK is currently missing some features found in other popular firmware. This tab
|
||||||
| Legend: | ✅ Supported | 🚧 Under Development | 💡 Planned |
|
| Legend: | ✅ Supported | 🚧 Under Development | 💡 Planned |
|
||||||
| :------ | :----------- | :------------------- | :--------- |
|
| :------ | :----------- | :------------------- | :--------- |
|
||||||
|
|
||||||
| **Feature** | ZMK | BlueMicro | QMK |
|
| **Feature** | ZMK | BlueMicro | QMK |
|
||||||
| ------------------------------------------------------------------------------------------------------------------------- | :-: | :-------: | :-: |
|
| ---------------------------------------------------------------------------------------------------------------------------------- | :-: | :-------: | :-: |
|
||||||
| Low Latency BLE Support | ✅ | ✅ | |
|
| Low Latency BLE Support | ✅ | ✅ | |
|
||||||
| Multi-Device BLE Support | ✅ | | |
|
| Multi-Device BLE Support | ✅ | | |
|
||||||
| [USB Connectivity](behaviors/outputs) | ✅ | ✅ | ✅ |
|
| [USB Connectivity](behaviors/outputs.md) | ✅ | ✅ | ✅ |
|
||||||
| User Configuration Repositories | ✅ | | |
|
| User Configuration Repositories | ✅ | | |
|
||||||
| Split Keyboard Support | ✅ | ✅ | ✅ |
|
| Split Keyboard Support | ✅ | ✅ | ✅ |
|
||||||
| [Keymaps and Layers](behaviors/layers) | ✅ | ✅ | ✅ |
|
| [Keymaps and Layers](behaviors/layers.md) | ✅ | ✅ | ✅ |
|
||||||
| [Hold-Tap](behaviors/hold-tap) (which includes [Mod-Tap](behaviors/mod-tap) and [Layer-Tap](behaviors/layers/#layer-tap)) | ✅ | ✅ | ✅ |
|
| [Hold-Tap](behaviors/hold-tap.md) (which includes [Mod-Tap](behaviors/mod-tap.md) and [Layer-Tap](behaviors/layers.md/#layer-tap)) | ✅ | ✅ | ✅ |
|
||||||
| [Keyboard Codes](codes/#keyboard) | ✅ | ✅ | ✅ |
|
| [Keyboard Codes](codes/index.mdx#keyboard) | ✅ | ✅ | ✅ |
|
||||||
| [Media](codes/#media-controls) & [Consumer](codes/#consumer-controls) Codes | ✅ | ✅ | ✅ |
|
| [Media](codes/index.mdx#media-controls) & [Consumer](codes/index.mdx#consumer-controls) Codes | ✅ | ✅ | ✅ |
|
||||||
| [Encoders](features/encoders)[^1] | ✅ | ✅ | ✅ |
|
| [Encoders](features/encoders.md)[^1] | ✅ | ✅ | ✅ |
|
||||||
| [Display Support](features/displays)[^2] | 🚧 | 🚧 | ✅ |
|
| [Display Support](features/displays.md)[^2] | 🚧 | 🚧 | ✅ |
|
||||||
| [RGB Underglow](features/underglow) | ✅ | ✅ | ✅ |
|
| [RGB Underglow](features/underglow.md) | ✅ | ✅ | ✅ |
|
||||||
| One Shot Keys | ✅ | ✅ | ✅ |
|
| One Shot Keys | ✅ | ✅ | ✅ |
|
||||||
| [Combo Keys](features/combos) | ✅ | | ✅ |
|
| [Combo Keys](features/combos.md) | ✅ | | ✅ |
|
||||||
| Macros | 🚧 | ✅ | ✅ |
|
| Macros | 🚧 | ✅ | ✅ |
|
||||||
| Mouse Keys | 💡 | ✅ | ✅ |
|
| Mouse Keys | 💡 | ✅ | ✅ |
|
||||||
| Low Active Power Usage | ✅ | | |
|
| Low Active Power Usage | ✅ | | |
|
||||||
| Low Power Sleep States | ✅ | ✅ | |
|
| Low Power Sleep States | ✅ | ✅ | |
|
||||||
| [Low Power Mode (VCC Shutoff)](behaviors/power) | ✅ | ✅ | |
|
| [Low Power Mode (VCC Shutoff)](behaviors/power.md) | ✅ | ✅ | |
|
||||||
| Battery Reporting | ✅ | ✅ | |
|
| Battery Reporting | ✅ | ✅ | |
|
||||||
| Shell over BLE | 💡 | | |
|
| Shell over BLE | 💡 | | |
|
||||||
| Realtime Keymap Updating | 💡 | | ✅ |
|
| Realtime Keymap Updating | 💡 | | ✅ |
|
||||||
| AVR/8 Bit | | | ✅ |
|
| AVR/8 Bit | | | ✅ |
|
||||||
| [Wide Range of ARM Chips Supported](https://docs.zephyrproject.org/latest/boards/index.html) | ✅ | | |
|
| [Wide Range of ARM Chips Supported](https://docs.zephyrproject.org/latest/boards/index.html) | ✅ | | |
|
||||||
|
|
||||||
[^2]: Encoders are not currently supported on peripheral side splits.
|
[^2]: Encoders are not currently supported on peripheral side splits.
|
||||||
[^1]: OLEDs are currently proof of concept in ZMK.
|
[^1]: OLEDs are currently proof of concept in ZMK.
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
// | GUI | DEL | RETURN | SPACE | ESCAPE | | RETURN | SPACE | TAB | BSPC | R ALT |
|
// | GUI | DEL | RETURN | SPACE | ESCAPE | | RETURN | SPACE | TAB | BSPC | R ALT |
|
||||||
bindings = <
|
bindings = <
|
||||||
&kp ESC &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp BSLH
|
&kp ESC &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp BSLH
|
||||||
&kp TAB &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp QUOTE
|
&kp TAB &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp SQT
|
||||||
&kp LSHIFT &kp Z &kp X &kp C &kp V &kp B &kp LC(A) &kp LC(C) &kp LC(V) &kp LC(X) &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp RCTRL
|
&kp LSHIFT &kp Z &kp X &kp C &kp V &kp B &kp LC(A) &kp LC(C) &kp LC(V) &kp LC(X) &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp RCTRL
|
||||||
&kp LGUI &kp DEL &kp RET &kp SPACE &kp ESC &kp RET &kp SPACE &kp TAB &kp BSPC &kp RALT
|
&kp LGUI &kp DEL &kp RET &kp SPACE &kp ESC &kp RET &kp SPACE &kp TAB &kp BSPC &kp RALT
|
||||||
>;
|
>;
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
// | GUI | DEL | RETURN | SPACE | ESCAPE | | RETURN | SPACE | TAB | BSPC | R ALT |
|
// | GUI | DEL | RETURN | SPACE | ESCAPE | | RETURN | SPACE | TAB | BSPC | R ALT |
|
||||||
bindings = <
|
bindings = <
|
||||||
&kp ESC &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp BSLH
|
&kp ESC &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp BSLH
|
||||||
&kp TAB &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp QUOTE
|
&kp TAB &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp SQT
|
||||||
&kp LSHIFT &kp Z &kp X &kp C &kp V &kp B &kp LC(A) &kp LC(C) &kp LC(V) &kp LC(X) &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp RCTRL
|
&kp LSHIFT &kp Z &kp X &kp C &kp V &kp B &kp LC(A) &kp LC(C) &kp LC(V) &kp LC(X) &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp RCTRL
|
||||||
&kp LGUI &kp DEL &kp RET &kp SPACE &kp ESC &kp RET &kp SPACE &kp TAB &kp BSPC &kp RALT
|
&kp LGUI &kp DEL &kp RET &kp SPACE &kp ESC &kp RET &kp SPACE &kp TAB &kp BSPC &kp RALT
|
||||||
>;
|
>;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue