Merge remote-tracking branch 'upstream/main' into Zodiark
This commit is contained in:
commit
958616990f
148 changed files with 18998 additions and 7415 deletions
13
.github/workflows/doc-checks.yml
vendored
13
.github/workflows/doc-checks.yml
vendored
|
@ -31,3 +31,16 @@ jobs:
|
|||
- name: Prettier check
|
||||
run: npm run prettier:check
|
||||
working-directory: docs
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: bahmutov/npm-install@v1
|
||||
with:
|
||||
working-directory: docs
|
||||
- name: Build
|
||||
run: npm run build
|
||||
working-directory: docs
|
||||
- name: TypeScript check
|
||||
run: npm run typecheck
|
||||
working-directory: docs
|
||||
|
|
45
.github/workflows/hardware-metadata-validation.yml
vendored
Normal file
45
.github/workflows/hardware-metadata-validation.yml
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
name: Hardware Metadata Validation
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- ".github/workflows/hardware-metadata-validation.yml"
|
||||
- "schema/hardware-metadata.schema.json"
|
||||
- "app/boards/**/*.zmk.yml"
|
||||
- "app/scripts/west_commands/metadata.py"
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/hardware-metadata-validation.yml"
|
||||
- "schema/hardware-metadata.schema.json"
|
||||
- "app/boards/**/*.zmk.yml"
|
||||
- "app/scripts/west_commands/metadata.py"
|
||||
|
||||
jobs:
|
||||
check-metadata-format:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v2
|
||||
- uses: bahmutov/npm-install@v1
|
||||
with:
|
||||
working-directory: app
|
||||
- name: Prettier Check
|
||||
run: npm run prettier:check
|
||||
working-directory: app
|
||||
validate-metadata:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: zmkfirmware/zmk-dev-arm:2.5
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install dependencies
|
||||
run: pip install -r app/scripts/requirements.txt
|
||||
- name: West init
|
||||
run: west init -l app
|
||||
- name: Update modules (west update)
|
||||
run: west update
|
||||
- name: Export Zephyr CMake package (west zephyr-export)
|
||||
run: west zephyr-export
|
||||
- name: Validate Hardware Metadata
|
||||
working-directory: app
|
||||
run: west metadata check
|
|
@ -91,7 +91,7 @@ You can setup git to run prettier automatically when you commit by installing th
|
|||
### Development Setup
|
||||
|
||||
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.
|
||||
|
||||
### Formatting
|
||||
|
|
1
app/.gitignore
vendored
1
app/.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
build/
|
||||
node_modules/
|
||||
|
|
3
app/.prettierrc.js
Normal file
3
app/.prettierrc.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
endOfLine: "auto",
|
||||
};
|
|
@ -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/keycode_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_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)
|
||||
|
|
|
@ -267,7 +267,7 @@ choice SYS_PM_POLICY
|
|||
default PM_POLICY_APP
|
||||
endchoice
|
||||
|
||||
config DEVICE_POWER_MANAGEMENT
|
||||
config PM_DEVICE
|
||||
default y
|
||||
|
||||
config ZMK_IDLE_SLEEP_TIMEOUT
|
||||
|
|
11
app/boards/arm/bdn9/bdn9_rev2.yml
Normal file
11
app/boards/arm/bdn9/bdn9_rev2.yml
Normal file
|
@ -0,0 +1,11 @@
|
|||
file_format: "1"
|
||||
id: bdn9_rev2
|
||||
name: BDN9 Rev2
|
||||
type: board
|
||||
arch: arm
|
||||
features:
|
||||
- keys
|
||||
- encoder
|
||||
outputs:
|
||||
- usb
|
||||
url: https://keeb.io/products/bdn9-rev-2-3x3-9-key-macropad-rotary-encoder-and-rgb
|
11
app/boards/arm/bdn9/bdn9_rev2.zmk.yml
Normal file
11
app/boards/arm/bdn9/bdn9_rev2.zmk.yml
Normal file
|
@ -0,0 +1,11 @@
|
|||
file_format: "1"
|
||||
id: bdn9_rev2
|
||||
name: BDN9 Rev2
|
||||
type: board
|
||||
arch: arm
|
||||
outputs:
|
||||
- usb
|
||||
features:
|
||||
- keys
|
||||
- encoder
|
||||
url: https://keeb.io/collections/bdn9-collection/products/bdn9-rev-2-3x3-9-key-macropad-rotary-encoder-and-rgb
|
10
app/boards/arm/bluemicro840/bluemicro840_v1.zmk.yml
Normal file
10
app/boards/arm/bluemicro840/bluemicro840_v1.zmk.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
file_format: "1"
|
||||
id: bluemicro840_v1
|
||||
name: BlueMicro840 v1
|
||||
type: board
|
||||
arch: arm
|
||||
outputs:
|
||||
- usb
|
||||
- ble
|
||||
url: https://nrf52.jpconstantineau.com/docs/bluemicro840_v1/
|
||||
exposes: [pro_micro]
|
8
app/boards/arm/ferris/Kconfig.board
Normal file
8
app/boards/arm/ferris/Kconfig.board
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Ferris board configuration
|
||||
|
||||
# Copyright (c) 2020 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
config BOARD_FERRIS
|
||||
bool "Ferris rev 0.2"
|
||||
depends on SOC_STM32F072XB
|
23
app/boards/arm/ferris/Kconfig.defconfig
Normal file
23
app/boards/arm/ferris/Kconfig.defconfig
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Ferris board configuration
|
||||
|
||||
# Copyright (c) 2020 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
if BOARD_FERRIS
|
||||
|
||||
config BOARD
|
||||
default "ferris_rev02"
|
||||
|
||||
config ZMK_KEYBOARD_NAME
|
||||
default "Ferris rev 0.2"
|
||||
|
||||
config ZMK_USB
|
||||
default y
|
||||
|
||||
config ZMK_KSCAN_MATRIX_POLLING
|
||||
default y
|
||||
|
||||
config ZMK_KSCAN_COMPOSITE_DRIVER
|
||||
default y
|
||||
|
||||
endif # BOARD_FERRIS
|
16
app/boards/arm/ferris/README.md
Normal file
16
app/boards/arm/ferris/README.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Building ZMK for the Ferris 0.2
|
||||
|
||||
|
||||
## Standard Build
|
||||
|
||||
```
|
||||
west build -p -d build/ferris --board ferris_rev02
|
||||
```
|
||||
|
||||
## Flashing
|
||||
|
||||
`west` can be used to flash the board directly. Press the reset button once, and run:
|
||||
|
||||
```
|
||||
west flash -d build/ferris
|
||||
```
|
7
app/boards/arm/ferris/board.cmake
Normal file
7
app/boards/arm/ferris/board.cmake
Normal file
|
@ -0,0 +1,7 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
board_runner_args(dfu-util "--pid=0483:df11" "--alt=0" "--dfuse")
|
||||
board_runner_args(jlink "--device=STM32F072CB" "--speed=4000")
|
||||
|
||||
include(${ZEPHYR_BASE}/boards/common/dfu-util.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake)
|
139
app/boards/arm/ferris/ferris_rev02.dts
Normal file
139
app/boards/arm/ferris/ferris_rev02.dts
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
#include <st/f0/stm32f072Xb.dtsi>
|
||||
#include <st/f0/stm32f072v(8-b)tx-pinctrl.dtsi>
|
||||
|
||||
#include <dt-bindings/zmk/matrix_transform.h>
|
||||
|
||||
/ {
|
||||
model = "Ferris rev0.2";
|
||||
compatible = "ferris,rev02", "st,stm32f072";
|
||||
|
||||
chosen {
|
||||
zephyr,sram = &sram0;
|
||||
zephyr,flash = &flash0;
|
||||
zmk,kscan = &kscan;
|
||||
zmk,matrix_transform = &transform;
|
||||
/* TODO: Enable once we support the IC for underglow
|
||||
zmk,underglow = &led_strip;
|
||||
*/
|
||||
};
|
||||
|
||||
transform: transform {
|
||||
compatible = "zmk,matrix-transform";
|
||||
rows = <4>;
|
||||
columns = <10>;
|
||||
|
||||
map = <
|
||||
RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9)
|
||||
RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9)
|
||||
RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,7) RC(2,8) RC(2,9)
|
||||
RC(3,3) RC(3,4) RC(3,5) RC(3,6)
|
||||
>;
|
||||
};
|
||||
|
||||
kscan: kscan {
|
||||
compatible = "zmk,kscan-composite";
|
||||
label = "KSCAN";
|
||||
rows = <4>;
|
||||
columns = <10>;
|
||||
|
||||
left {
|
||||
kscan = <&kscan_left>;
|
||||
};
|
||||
|
||||
right {
|
||||
kscan = <&kscan_right>;
|
||||
column-offset = <5>;
|
||||
};
|
||||
};
|
||||
|
||||
kscan_left: kscan_left {
|
||||
compatible = "zmk,kscan-gpio-matrix";
|
||||
label = "KSCAN_LEFT";
|
||||
|
||||
diode-direction = "col2row";
|
||||
|
||||
col-gpios
|
||||
= <&gpiob 8 (GPIO_ACTIVE_HIGH)>
|
||||
, <&gpiob 4 (GPIO_ACTIVE_HIGH)>
|
||||
, <&gpiob 3 (GPIO_ACTIVE_HIGH)>
|
||||
, <&gpioa 15 (GPIO_ACTIVE_HIGH)>
|
||||
, <&gpioa 14 (GPIO_ACTIVE_HIGH)>
|
||||
;
|
||||
row-gpios
|
||||
= <&gpiob 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
, <&gpiob 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
, <&gpiob 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
, <&gpioa 2 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
|
||||
;
|
||||
};
|
||||
|
||||
kscan_right: kscan_right {
|
||||
compatible = "zmk,kscan-gpio-matrix";
|
||||
label = "KSCAN_RIGHT";
|
||||
|
||||
diode-direction = "row2col";
|
||||
|
||||
col-gpios
|
||||
= <&right_io 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
, <&right_io 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
, <&right_io 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
, <&right_io 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
, <&right_io 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
|
||||
;
|
||||
row-gpios
|
||||
= <&right_io 8 (GPIO_ACTIVE_LOW)>
|
||||
, <&right_io 9 (GPIO_ACTIVE_LOW)>
|
||||
, <&right_io 10 (GPIO_ACTIVE_LOW)>
|
||||
, <&right_io 11 (GPIO_ACTIVE_LOW)>
|
||||
;
|
||||
};
|
||||
};
|
||||
|
||||
&i2c2 {
|
||||
pinctrl-0 = <&i2c2_scl_pb10 &i2c2_sda_pb11>;
|
||||
status = "okay";
|
||||
clock-frequency = <I2C_BITRATE_FAST>;
|
||||
|
||||
right_io: mcp23017@20 {
|
||||
compatible = "microchip,mcp23017";
|
||||
status = "okay";
|
||||
gpio-controller;
|
||||
reg = <0x20>;
|
||||
label = "RIGHT_IO";
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <16>;
|
||||
};
|
||||
};
|
||||
|
||||
&usb {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&rtc {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&flash0 {
|
||||
/*
|
||||
* For more information, see:
|
||||
* http: //docs.zephyrproject.org/latest/guides/dts/index.html#flash-partitions
|
||||
*/
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
/* Set 6Kb of storage at the end of the 128Kb of flash */
|
||||
storage_partition: partition@3e800 {
|
||||
label = "storage";
|
||||
reg = <0x0001e800 0x00001800>;
|
||||
};
|
||||
};
|
||||
};
|
79
app/boards/arm/ferris/ferris_rev02.keymap
Normal file
79
app/boards/arm/ferris/ferris_rev02.keymap
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <dt-bindings/zmk/bt.h>
|
||||
|
||||
#define NAV_L 1
|
||||
#define OTHER_L 2
|
||||
#define NUM_L 3
|
||||
#define SYM_L 4
|
||||
|
||||
// Using layer taps on thumbs, having quick tap as well helps w/ repeating space/backspace
|
||||
< { quick_tap_ms = <200>; };
|
||||
|
||||
/ {
|
||||
behaviors {
|
||||
hm: homerow_mods {
|
||||
compatible = "zmk,behavior-hold-tap";
|
||||
label = "homerow mods";
|
||||
#binding-cells = <2>;
|
||||
tapping_term_ms = <200>;
|
||||
flavor = "tap-preferred";
|
||||
bindings = <&kp>, <&kp>;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P
|
||||
&hm LGUI A &hm LALT S &hm LCTRL D &hm LSHFT F &kp G &kp H &hm RSHFT J &hm RCTRL K &hm LALT L &hm LGUI QUOT
|
||||
&kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH
|
||||
< NAV_L TAB &kp ENTER < NUM_L SPACE < SYM_L BKSP
|
||||
>;
|
||||
};
|
||||
|
||||
nav_layer {
|
||||
bindings = <
|
||||
&trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
|
||||
&trans &trans &trans &trans &trans &trans &kp LARW &kp DARW &kp UARW &kp RARW
|
||||
&trans &trans &trans &trans &trans &trans &kp HOME &kp PG_DN &kp PG_UP &kp END
|
||||
&trans &trans &kp ESC &kp DEL
|
||||
>;
|
||||
};
|
||||
|
||||
other_layer {
|
||||
bindings = <
|
||||
&trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
|
||||
&trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
|
||||
&trans &trans &trans &trans &trans &trans &kp HOME &trans &trans &trans
|
||||
&trans &trans &trans &trans
|
||||
>;
|
||||
};
|
||||
|
||||
num_layer {
|
||||
bindings = <
|
||||
&kp LBKT &kp N7 &kp N8 &kp N9 &kp RBKT &trans &trans &trans &trans &trans
|
||||
&kp SEMI &kp N4 &kp N5 &kp N6 &kp EQUAL &trans &trans &trans &trans &trans
|
||||
&kp GRAVE &kp N1 &kp N2 &kp N3 &kp BSLH &trans &trans &trans &trans &trans
|
||||
&kp N0 &kp MINUS &trans &trans
|
||||
>;
|
||||
};
|
||||
|
||||
sym_layer {
|
||||
bindings = <
|
||||
&kp LBRC &kp LS(N7) &kp LS(N8) &kp LS(N9) &kp RBRC &trans &trans &trans &trans &trans
|
||||
&kp COLON &kp LS(N4) &kp LS(N5) &kp LS(N6) &kp PLUS &trans &trans &trans &trans &trans
|
||||
&kp TILDE &kp LS(N1) &kp LS(N2) &kp LS(N3) &kp LS(BSLH) &trans &trans &trans &trans &trans
|
||||
&kp LS(N0) &kp UNDER &trans &trans
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
12
app/boards/arm/ferris/ferris_rev02.yaml
Normal file
12
app/boards/arm/ferris/ferris_rev02.yaml
Normal file
|
@ -0,0 +1,12 @@
|
|||
identifier: ferris_rev02
|
||||
name: Ferris 0.2
|
||||
type: mcu
|
||||
arch: arm
|
||||
toolchain:
|
||||
- zephyr
|
||||
- gnuarmemb
|
||||
- xtools
|
||||
ram: 40
|
||||
supported:
|
||||
- switches
|
||||
- underglow
|
10
app/boards/arm/ferris/ferris_rev02.zmk.yml
Normal file
10
app/boards/arm/ferris/ferris_rev02.zmk.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
file_format: "1"
|
||||
id: ferris_rev02
|
||||
name: Ferris 0.2
|
||||
type: board
|
||||
arch: arm
|
||||
features:
|
||||
- keys
|
||||
outputs:
|
||||
- usb
|
||||
url: https://github.com/pierrechevalier83/ferris/tree/main/0.2
|
43
app/boards/arm/ferris/ferris_rev02_defconfig
Normal file
43
app/boards/arm/ferris/ferris_rev02_defconfig
Normal file
|
@ -0,0 +1,43 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
CONFIG_BOARD_FERRIS=y
|
||||
CONFIG_SOC_SERIES_STM32F0X=y
|
||||
CONFIG_SOC_STM32F072XB=y
|
||||
# 48MHz system clock
|
||||
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=48000000
|
||||
|
||||
# enable PINMUX
|
||||
CONFIG_PINMUX=y
|
||||
|
||||
# enable GPIO
|
||||
CONFIG_GPIO=y
|
||||
|
||||
# Enable i2c
|
||||
CONFIG_I2C=y
|
||||
|
||||
# ZMK Settings
|
||||
CONFIG_ZMK_USB=y
|
||||
CONFIG_ZMK_KSCAN_GPIO_DRIVER=y
|
||||
CONFIG_ZMK_KSCAN_COMPOSITE_DRIVER=y
|
||||
CONFIG_ZMK_KSCAN_MATRIX_POLLING=y
|
||||
CONFIG_USB_SELF_POWERED=n
|
||||
|
||||
# Enable IO multiplexer
|
||||
CONFIG_GPIO_MCP23017=y
|
||||
|
||||
# Needed to reduce this to size that will fit on F072
|
||||
CONFIG_HEAP_MEM_POOL_SIZE=1024
|
||||
|
||||
# clock configuration
|
||||
CONFIG_CLOCK_CONTROL=y
|
||||
|
||||
# Clock configuration for Cube Clock control driver
|
||||
CONFIG_CLOCK_STM32_SYSCLK_SRC_PLL=y
|
||||
# use HSI as PLL input
|
||||
CONFIG_CLOCK_STM32_PLL_SRC_HSI=y
|
||||
# produce 48MHz clock at PLL output
|
||||
# CONFIG_CLOCK_STM32_PLL_PREDIV=1
|
||||
CONFIG_CLOCK_STM32_PLL_MULTIPLIER=6
|
||||
CONFIG_CLOCK_STM32_AHB_PRESCALER=1
|
||||
CONFIG_CLOCK_STM32_APB1_PRESCALER=1
|
||||
# CONFIG_CLOCK_STM32_APB2_PRESCALER=1
|
12
app/boards/arm/nice60/nice60.zmk.yml
Normal file
12
app/boards/arm/nice60/nice60.zmk.yml
Normal file
|
@ -0,0 +1,12 @@
|
|||
file_format: "1"
|
||||
id: nice60
|
||||
name: nice!60
|
||||
type: board
|
||||
arch: arm
|
||||
features:
|
||||
- keys
|
||||
- underglow
|
||||
outputs:
|
||||
- usb
|
||||
- ble
|
||||
url: https://nicekeyboards.com/nice-60
|
|
@ -25,7 +25,18 @@ config ZMK_BLE
|
|||
config ZMK_USB
|
||||
default y
|
||||
|
||||
endif # BOARD_NICE_NANO || BOARD_NICE_NANO_V2
|
||||
|
||||
if BOARD_NICE_NANO
|
||||
|
||||
config ZMK_BATTERY_VOLTAGE_DIVIDER
|
||||
default y
|
||||
|
||||
endif # BOARD_NICE_NANO || BOARD_NICE_NANO_V2
|
||||
endif # BOARD_NICE_NANO
|
||||
|
||||
if BOARD_NICE_NANO_V2
|
||||
|
||||
config ZMK_BATTERY_NRF_VDDH
|
||||
default y
|
||||
|
||||
endif # BOARD_NICE_NANO_V2
|
||||
|
|
10
app/boards/arm/nice_nano/nice_nano.zmk.yml
Normal file
10
app/boards/arm/nice_nano/nice_nano.zmk.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
file_format: "1"
|
||||
id: nice_nano
|
||||
name: nice!nano v1
|
||||
type: board
|
||||
arch: arm
|
||||
outputs:
|
||||
- usb
|
||||
- ble
|
||||
url: https://nicekeyboards.com/nice-nano
|
||||
exposes: [pro_micro]
|
|
@ -16,11 +16,7 @@
|
|||
};
|
||||
|
||||
vbatt {
|
||||
compatible = "zmk,battery-voltage-divider";
|
||||
compatible = "zmk,battery-nrf-vddh";
|
||||
label = "BATTERY";
|
||||
io-channels = <&adc (0x0D - 1)>;
|
||||
// Multiply ADC result by 5
|
||||
full-ohms = <5>;
|
||||
output-ohms = <1>;
|
||||
};
|
||||
};
|
||||
|
|
10
app/boards/arm/nice_nano/nice_nano_v2.zmk.yml
Normal file
10
app/boards/arm/nice_nano/nice_nano_v2.zmk.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
file_format: "1"
|
||||
id: nice_nano_v2
|
||||
name: nice!nano v2
|
||||
type: board
|
||||
arch: arm
|
||||
outputs:
|
||||
- usb
|
||||
- ble
|
||||
url: https://nicekeyboards.com/nice-nano
|
||||
exposes: [pro_micro]
|
10
app/boards/arm/nrf52840_m2/nrf52840_m2.zmk.yml
Normal file
10
app/boards/arm/nrf52840_m2/nrf52840_m2.zmk.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
file_format: "1"
|
||||
id: nrf52840_m2
|
||||
name: nRF52840 M.2 Module
|
||||
type: board
|
||||
arch: arm
|
||||
outputs:
|
||||
- usb
|
||||
- ble
|
||||
url: https://wiki.makerdiary.com/nrf52840-m2/
|
||||
exposes: [makerdiary_nrf52840_m2]
|
10
app/boards/arm/nrfmicro/nrfmicro_11.zmk.yml
Normal file
10
app/boards/arm/nrfmicro/nrfmicro_11.zmk.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
file_format: "1"
|
||||
id: nrfmicro_11
|
||||
name: nRFMicro 1.1/1.2
|
||||
type: board
|
||||
arch: arm
|
||||
outputs:
|
||||
- usb
|
||||
- ble
|
||||
url: https://github.com/joric/nrfmicro/
|
||||
exposes: [pro_micro]
|
10
app/boards/arm/nrfmicro/nrfmicro_11_flipped.zmk.yml
Normal file
10
app/boards/arm/nrfmicro/nrfmicro_11_flipped.zmk.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
file_format: "1"
|
||||
id: nrfmicro_11_flipped
|
||||
name: nRFMicro 1.1 (flipped)
|
||||
type: board
|
||||
arch: arm
|
||||
outputs:
|
||||
- usb
|
||||
- ble
|
||||
url: https://github.com/joric/nrfmicro/
|
||||
exposes: [pro_micro]
|
10
app/boards/arm/nrfmicro/nrfmicro_13.zmk.yml
Normal file
10
app/boards/arm/nrfmicro/nrfmicro_13.zmk.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
file_format: "1"
|
||||
id: nrfmicro_13
|
||||
name: nRFMicro 1.3/1.4
|
||||
type: board
|
||||
arch: arm
|
||||
outputs:
|
||||
- usb
|
||||
- ble
|
||||
url: https://github.com/joric/nrfmicro/
|
||||
exposes: [pro_micro]
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
/dts-v1/;
|
||||
#include <st/f3/stm32f303Xc.dtsi>
|
||||
#include <dt-bindings/zmk/matrix_transform.h>
|
||||
|
||||
/ {
|
||||
model = "Plack PCD, rev6";
|
||||
|
@ -15,11 +16,13 @@
|
|||
zephyr,sram = &sram0;
|
||||
zephyr,flash = &flash0;
|
||||
zmk,kscan = &kscan0;
|
||||
zmk,matrix_transform = &layout_grid_transform;
|
||||
};
|
||||
|
||||
kscan0: kscan {
|
||||
compatible = "zmk,kscan-gpio-matrix";
|
||||
label = "KSCAN";
|
||||
diode-direction = "col2row";
|
||||
row-gpios
|
||||
= <&gpioa 10 (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 {
|
||||
|
|
10
app/boards/arm/planck/planck_rev6.zmk.yml
Normal file
10
app/boards/arm/planck/planck_rev6.zmk.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
file_format: "1"
|
||||
id: planck_rev6
|
||||
name: Planck Rev6
|
||||
type: board
|
||||
arch: arm
|
||||
features:
|
||||
- keys
|
||||
outputs:
|
||||
- usb
|
||||
url: https://olkb.com/collections/planck
|
9
app/boards/arm/proton_c/proton_c.zmk.yml
Normal file
9
app/boards/arm/proton_c/proton_c.zmk.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
file_format: "1"
|
||||
id: proton_c
|
||||
name: QMK Proton-C
|
||||
type: board
|
||||
arch: arm
|
||||
outputs:
|
||||
- usb
|
||||
url: https://qmk.fm/proton-c/
|
||||
exposes: [pro_micro]
|
|
@ -0,0 +1,10 @@
|
|||
file_format: "1"
|
||||
id: makerdiary_nrf52840_m2
|
||||
name: MakerDiary nRF52840 M.2
|
||||
type: interconnect
|
||||
url: https://wiki.makerdiary.com/nrf52840-m2/
|
||||
manufacturer: MakerDiary
|
||||
description: |
|
||||
The MakerDiary nRF52840 M.2 module is a module using the M.2/NGFF form factor to expose a
|
||||
large number of GPIO pins, allowing use of a variety of peripherals such using I2C, SPI,
|
||||
etc.
|
10
app/boards/interconnects/pro_micro/pro_micro.zmk.yml
Normal file
10
app/boards/interconnects/pro_micro/pro_micro.zmk.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
file_format: "1"
|
||||
id: pro_micro
|
||||
name: Pro Micro
|
||||
type: interconnect
|
||||
url: https://www.sparkfun.com/products/12640
|
||||
manufacturer: SparkFun
|
||||
description: |
|
||||
The SparkFun Pro Micro grew popular as a low cost ATmega32U4 board with sufficient GPIO and peripherals
|
||||
to work for many keyboard needs. Since the original Pro Micro, many pin compatible boards have appeared,
|
||||
with various changes or improvements, such as the Elite-C w/ USB-C, nice!nano with nRF52840 wireless.
|
26
app/boards/shields/a_dux/Kconfig.defconfig
Normal file
26
app/boards/shields/a_dux/Kconfig.defconfig
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Copyright (c) 2021 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
if SHIELD_A_DUX_LEFT
|
||||
|
||||
config ZMK_KEYBOARD_NAME
|
||||
default "A. Dux"
|
||||
|
||||
config ZMK_SPLIT_BLE_ROLE_CENTRAL
|
||||
default y
|
||||
|
||||
endif
|
||||
|
||||
if SHIELD_A_DUX_RIGHT
|
||||
|
||||
config ZMK_KEYBOARD_NAME
|
||||
default "A. Dux Right"
|
||||
|
||||
endif
|
||||
|
||||
if SHIELD_A_DUX_LEFT || SHIELD_A_DUX_RIGHT
|
||||
|
||||
config ZMK_SPLIT
|
||||
default y
|
||||
|
||||
endif
|
8
app/boards/shields/a_dux/Kconfig.shield
Normal file
8
app/boards/shields/a_dux/Kconfig.shield
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2021 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
config SHIELD_A_DUX_LEFT
|
||||
def_bool $(shields_list_contains,a_dux_left)
|
||||
|
||||
config SHIELD_A_DUX_RIGHT
|
||||
def_bool $(shields_list_contains,a_dux_right)
|
15
app/boards/shields/a_dux/README.md
Normal file
15
app/boards/shields/a_dux/README.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# A. Dux
|
||||
|
||||
Shield configuration for [Architeuthis Dux by Tapi][1] (aka A. Dux, A.D., "Giant Squid").
|
||||
|
||||
![Wireless Architeuthis Dux with nice!nano controllers][2]
|
||||
|
||||
This shield is an adaptation of the direct pin [Cradio shield by @davidphilipbarr][3].
|
||||
|
||||
## Cephalopoda
|
||||
|
||||
Check out the rest of Tapi's Cephalopoda collection of low profile split ergonomic mechanical keyboards at <https://github.com/tapioki/cephalopoda>.
|
||||
|
||||
[1]: https://github.com/tapioki/cephalopoda/tree/main/Architeuthis%20dux
|
||||
[2]: https://media.discordapp.net/attachments/855822038287908864/866315666802081792/image0.jpg
|
||||
[3]: https://github.com/zmkfirmware/zmk/tree/main/app/boards/shields/cradio
|
2
app/boards/shields/a_dux/a_dux.conf
Normal file
2
app/boards/shields/a_dux/a_dux.conf
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Copyright (c) 2021 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
52
app/boards/shields/a_dux/a_dux.dtsi
Normal file
52
app/boards/shields/a_dux/a_dux.dtsi
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <dt-bindings/zmk/matrix_transform.h>
|
||||
|
||||
/ {
|
||||
|
||||
chosen {
|
||||
zmk,kscan = &kscan0;
|
||||
zmk,matrix_transform = &default_transform;
|
||||
};
|
||||
|
||||
default_transform: keymap_transform_0 {
|
||||
compatible = "zmk,matrix-transform";
|
||||
columns = <34>;
|
||||
rows = <1>;
|
||||
map = <
|
||||
RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,21) RC(0,20) RC(0,19) RC(0,18) RC(0,17)
|
||||
RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) RC(0,26) RC(0,25) RC(0,24) RC(0,23) RC(0,22)
|
||||
RC(0,10) RC(0,11) RC(0,12) RC(0,13) RC(0,14) RC(0,31) RC(0,30) RC(0,29) RC(0,28) RC(0,27)
|
||||
RC(0,15) RC(0,16) RC(0,33) RC(0,32)
|
||||
>;
|
||||
};
|
||||
|
||||
kscan0: kscan {
|
||||
compatible = "zmk,kscan-gpio-direct";
|
||||
label = "KSCAN";
|
||||
input-gpios =
|
||||
<&pro_micro_d 5 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>,
|
||||
<&pro_micro_d 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>,
|
||||
<&pro_micro_a 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>,
|
||||
<&pro_micro_d 16 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>,
|
||||
<&pro_micro_a 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>,
|
||||
<&pro_micro_d 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>,
|
||||
<&pro_micro_d 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>,
|
||||
<&pro_micro_a 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>,
|
||||
<&pro_micro_d 14 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>,
|
||||
<&pro_micro_a 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>,
|
||||
<&pro_micro_d 7 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>,
|
||||
<&pro_micro_d 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>,
|
||||
<&pro_micro_d 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>,
|
||||
<&pro_micro_d 15 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>,
|
||||
<&pro_micro_d 10 (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)>
|
||||
;
|
||||
};
|
||||
|
||||
};
|
26
app/boards/shields/a_dux/a_dux.keymap
Normal file
26
app/boards/shields/a_dux/a_dux.keymap
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/keys.h>
|
||||
|
||||
/ {
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
|
||||
// This is a sample keymap intended to be replaced with your own
|
||||
base_layer {
|
||||
bindings = <
|
||||
&kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P
|
||||
&kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI
|
||||
&kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp SLASH
|
||||
&kp TAB &kp BSPC &kp SPACE &kp ENTER
|
||||
>;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
11
app/boards/shields/a_dux/a_dux.zmk.yml
Normal file
11
app/boards/shields/a_dux/a_dux.zmk.yml
Normal file
|
@ -0,0 +1,11 @@
|
|||
file_format: "1"
|
||||
id: a_dux
|
||||
name: A. Dux
|
||||
type: shield
|
||||
url: https://github.com/tapioki/cephalopoda/tree/main/Architeuthis%20dux
|
||||
requires: [pro_micro]
|
||||
features:
|
||||
- keys
|
||||
siblings:
|
||||
- a_dux_left
|
||||
- a_dux_right
|
7
app/boards/shields/a_dux/a_dux_left.overlay
Normal file
7
app/boards/shields/a_dux/a_dux_left.overlay
Normal file
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "a_dux.dtsi"
|
11
app/boards/shields/a_dux/a_dux_right.overlay
Normal file
11
app/boards/shields/a_dux/a_dux_right.overlay
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "a_dux.dtsi"
|
||||
|
||||
&default_transform {
|
||||
col-offset = <17>;
|
||||
};
|
11
app/boards/shields/bfo9000/bfo9000.zmk.yml
Normal file
11
app/boards/shields/bfo9000/bfo9000.zmk.yml
Normal file
|
@ -0,0 +1,11 @@
|
|||
file_format: "1"
|
||||
id: bfo9000
|
||||
name: BFO-9000
|
||||
type: shield
|
||||
url: https://keeb.io/products/bfo-9000-keyboard-customizable-full-size-split-ortholinear
|
||||
requires: [pro_micro]
|
||||
features:
|
||||
- keys
|
||||
siblings:
|
||||
- bfo9000_left
|
||||
- bfo9000_right
|
8
app/boards/shields/boardsource3x4/boardsource3x4.zmk.yml
Normal file
8
app/boards/shields/boardsource3x4/boardsource3x4.zmk.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
file_format: "1"
|
||||
id: boardsource3x4
|
||||
name: Boardsource 3x4 Macropad
|
||||
type: shield
|
||||
url: https://boardsource.xyz/store/5ecc2008eee64242946c98c1
|
||||
requires: [pro_micro]
|
||||
features:
|
||||
- keys
|
|
@ -16,40 +16,40 @@
|
|||
// -----------------------------------------------------------------------------------------
|
||||
// | TAB | Q | W | E | R | T | | Y | U | I | O | P | BKSP |
|
||||
// | 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 |
|
||||
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 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
|
||||
>;
|
||||
};
|
||||
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 | | |
|
||||
// | SHFT | | | | | | | | | | | | |
|
||||
// | GUI | | SPC | | ENT | | ALT |
|
||||
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
|
||||
&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 LGUI &trans &kp SPACE &kp RET &trans &kp RALT
|
||||
&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
|
||||
&kp LSHFT &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
|
||||
&kp LGUI &trans &kp SPACE &kp RET &trans &kp RALT
|
||||
>;
|
||||
};
|
||||
|
||||
raise_layer {
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// | ESC | ! | @ | # | $ | % | | ^ | & | * | ( | ) | BKSP |
|
||||
// | CTRL | | | | | | | - | = | { | } | "|" | ` |
|
||||
// | SHFT | | | | | | | _ | + | [ | ] | \ | ~ | // TODO: Fix this row when &mkp is committed
|
||||
// | TAB | ! | @ | # | $ | % | | ^ | & | * | ( | ) | BKSP |
|
||||
// | CTRL | | | | | | | - | = | [ | ] | \ | ` |
|
||||
// | SHFT | | | | | | | _ | + | { | } | "|" | ~ |
|
||||
// | GUI | | SPC | | ENT | | ALT |
|
||||
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 LCTRL &trans &trans &trans &trans &trans &kp MINUS &kp EQUAL &kp LBKT &kp RBKT &kp PIPE &kp GRAVE
|
||||
&kp LSHFT &trans &trans &trans &trans &trans &trans &trans &trans &trans &kp BSLH &kp TILDE
|
||||
&kp LGUI &trans &kp SPACE &kp RET &trans &kp RALT
|
||||
&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 BSLH &kp GRAVE
|
||||
&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
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
|
14
app/boards/shields/corne/corne.zmk.yml
Normal file
14
app/boards/shields/corne/corne.zmk.yml
Normal file
|
@ -0,0 +1,14 @@
|
|||
file_format: "1"
|
||||
id: corne
|
||||
name: Corne
|
||||
type: shield
|
||||
url: https://github.com/foostan/crkbd/
|
||||
requires: [pro_micro]
|
||||
exposes: [i2c_oled]
|
||||
features:
|
||||
- keys
|
||||
- display
|
||||
- underglow
|
||||
siblings:
|
||||
- corne_left
|
||||
- corne_right
|
|
@ -1 +1,35 @@
|
|||
# Cradio
|
||||
|
||||
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 (`{...}`).
|
||||
|
|
12
app/boards/shields/cradio/cradio.zmk.yml
Normal file
12
app/boards/shields/cradio/cradio.zmk.yml
Normal file
|
@ -0,0 +1,12 @@
|
|||
file_format: "1"
|
||||
id: cradio
|
||||
name: Cradio/Sweep
|
||||
type: shield
|
||||
url: https://github.com/davidphilipbarr/Sweep
|
||||
requires: [pro_micro]
|
||||
exposes: [i2c_oled]
|
||||
features:
|
||||
- keys
|
||||
siblings:
|
||||
- cradio_left
|
||||
- cradio_right
|
9
app/boards/shields/crbn/crbn.zmk.yml
Normal file
9
app/boards/shields/crbn/crbn.zmk.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
file_format: "1"
|
||||
id: crbn
|
||||
name: CRBN Featherlight
|
||||
type: shield
|
||||
url: https://github.com/PolarityWorks/CRBN-Featherlight
|
||||
requires: [pro_micro]
|
||||
features:
|
||||
- keys
|
||||
- encoder
|
8
app/boards/shields/eek/eek.zmk.yml
Normal file
8
app/boards/shields/eek/eek.zmk.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
file_format: "1"
|
||||
id: eek
|
||||
name: eek!
|
||||
type: shield
|
||||
url: https://github.com/Klackygears/eek_doc
|
||||
requires: [pro_micro]
|
||||
features:
|
||||
- keys
|
13
app/boards/shields/helix/helix.zmk.yml
Normal file
13
app/boards/shields/helix/helix.zmk.yml
Normal file
|
@ -0,0 +1,13 @@
|
|||
file_format: "1"
|
||||
id: helix
|
||||
name: Helix
|
||||
type: shield
|
||||
url: https://github.com/MakotoKurauchi/helix
|
||||
requires: [pro_micro]
|
||||
exposes: [i2c_oled]
|
||||
features:
|
||||
- keys
|
||||
- display
|
||||
siblings:
|
||||
- helix_left
|
||||
- helix_right
|
14
app/boards/shields/iris/iris.zmk.yml
Normal file
14
app/boards/shields/iris/iris.zmk.yml
Normal file
|
@ -0,0 +1,14 @@
|
|||
file_format: "1"
|
||||
id: iris
|
||||
name: Iris
|
||||
type: shield
|
||||
url: https://keeb.io/products/iris-keyboard-split-ergonomic-keyboard
|
||||
requires: [pro_micro]
|
||||
exposes: [i2c_oled]
|
||||
features:
|
||||
- keys
|
||||
- display
|
||||
- encoder
|
||||
siblings:
|
||||
- iris_left
|
||||
- iris_right
|
11
app/boards/shields/jian/jian.zmk.yml
Normal file
11
app/boards/shields/jian/jian.zmk.yml
Normal file
|
@ -0,0 +1,11 @@
|
|||
file_format: "1"
|
||||
id: jian
|
||||
name: Jian
|
||||
type: shield
|
||||
url: https://github.com/KGOH/Jian-Info
|
||||
requires: [pro_micro]
|
||||
features:
|
||||
- keys
|
||||
siblings:
|
||||
- jian_left
|
||||
- jian_right
|
14
app/boards/shields/jorne/jorne.zmk.yml
Normal file
14
app/boards/shields/jorne/jorne.zmk.yml
Normal file
|
@ -0,0 +1,14 @@
|
|||
file_format: "1"
|
||||
id: jorne
|
||||
name: Jorne
|
||||
type: shield
|
||||
url: https://github.com/joric/jorne
|
||||
requires: [pro_micro]
|
||||
exposes: [i2c_oled]
|
||||
features:
|
||||
- keys
|
||||
- display
|
||||
- underglow
|
||||
siblings:
|
||||
- jorne_left
|
||||
- jorne_right
|
15
app/boards/shields/kyria/kyria.zmk.yml
Normal file
15
app/boards/shields/kyria/kyria.zmk.yml
Normal file
|
@ -0,0 +1,15 @@
|
|||
file_format: "1"
|
||||
id: kyria
|
||||
name: Kyria
|
||||
type: shield
|
||||
url: https://splitkb.com/products/kyria-pcb-kit
|
||||
requires: [pro_micro]
|
||||
exposes: [i2c_oled]
|
||||
features:
|
||||
- keys
|
||||
- display
|
||||
- encoder
|
||||
- underglow
|
||||
siblings:
|
||||
- kyria_left
|
||||
- kyria_right
|
13
app/boards/shields/lily58/lily58.zmk.yml
Normal file
13
app/boards/shields/lily58/lily58.zmk.yml
Normal file
|
@ -0,0 +1,13 @@
|
|||
file_format: "1"
|
||||
id: lily58
|
||||
name: Lily58
|
||||
type: shield
|
||||
url: https://github.com/kata0510/Lily58
|
||||
requires: [pro_micro]
|
||||
exposes: [i2c_oled]
|
||||
features:
|
||||
- keys
|
||||
- display
|
||||
siblings:
|
||||
- lily58_left
|
||||
- lily58_right
|
8
app/boards/shields/m60/m60.zmk.yml
Normal file
8
app/boards/shields/m60/m60.zmk.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
file_format: "1"
|
||||
id: m60
|
||||
name: MakerDiary m60
|
||||
type: shield
|
||||
url: https://makerdiary.com/pages/m60-mechanical-keyboard
|
||||
requires: [makerdiary_nrf52840_m2]
|
||||
features:
|
||||
- keys
|
13
app/boards/shields/microdox/microdox.zmk.yml
Normal file
13
app/boards/shields/microdox/microdox.zmk.yml
Normal file
|
@ -0,0 +1,13 @@
|
|||
file_format: "1"
|
||||
id: microdox
|
||||
name: Microdox
|
||||
type: shield
|
||||
url: https://boardsource.xyz/store/5f2e7e4a2902de7151494f92
|
||||
requires: [pro_micro]
|
||||
exposes: [i2c_oled]
|
||||
features:
|
||||
- keys
|
||||
- display
|
||||
siblings:
|
||||
- microdox_left
|
||||
- microdox_right
|
9
app/boards/shields/nibble/nibble.zmk.yml
Normal file
9
app/boards/shields/nibble/nibble.zmk.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
file_format: "1"
|
||||
id: nibble
|
||||
name: Nibble
|
||||
type: shield
|
||||
url: https://nullbits.co/nibble/
|
||||
requires: [pro_micro]
|
||||
features:
|
||||
- keys
|
||||
- encoder
|
8
app/boards/shields/qaz/qaz.zmk.yml
Normal file
8
app/boards/shields/qaz/qaz.zmk.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
file_format: "1"
|
||||
id: qaz
|
||||
name: QAZ
|
||||
type: shield
|
||||
url: https://www.cbkbd.com/product/qaz-keyboard-kit
|
||||
requires: [pro_micro]
|
||||
features:
|
||||
- keys
|
12
app/boards/shields/quefrency/quefrency.zmk.yml
Normal file
12
app/boards/shields/quefrency/quefrency.zmk.yml
Normal file
|
@ -0,0 +1,12 @@
|
|||
file_format: "1"
|
||||
id: quefrency
|
||||
name: Quefrency Rev. 1
|
||||
type: shield
|
||||
url: https://github.com/keebio/quefrency-rev1-pcb
|
||||
requires: [pro_micro]
|
||||
features:
|
||||
- keys
|
||||
- encoder
|
||||
siblings:
|
||||
- quenfrency_left
|
||||
- quenfrency_right
|
8
app/boards/shields/reviung41/reviung41.zmk.yml
Normal file
8
app/boards/shields/reviung41/reviung41.zmk.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
file_format: "1"
|
||||
id: reviung41
|
||||
name: REVIUNG41
|
||||
type: shield
|
||||
url: https://github.com/gtips/reviung/tree/master/reviung41
|
||||
requires: [pro_micro]
|
||||
features:
|
||||
- keys
|
8
app/boards/shields/romac/romac.zmk.yml
Normal file
8
app/boards/shields/romac/romac.zmk.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
file_format: "1"
|
||||
id: romac
|
||||
name: Romac Macropad
|
||||
type: shield
|
||||
url: https://mechboards.co.uk/shop/kits/romac-macro-pad/
|
||||
requires: [pro_micro]
|
||||
features:
|
||||
- keys
|
10
app/boards/shields/romac_plus/romac_plus.zmk.yml
Normal file
10
app/boards/shields/romac_plus/romac_plus.zmk.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
file_format: "1"
|
||||
id: romac_plus
|
||||
name: Romac+ Macropad
|
||||
type: shield
|
||||
url: https://example.org
|
||||
requires: [pro_micro]
|
||||
features:
|
||||
- keys
|
||||
- encoder
|
||||
- display
|
14
app/boards/shields/sofle/sofle.zmk.yml
Normal file
14
app/boards/shields/sofle/sofle.zmk.yml
Normal file
|
@ -0,0 +1,14 @@
|
|||
file_format: "1"
|
||||
id: sofle
|
||||
name: Sofle
|
||||
type: shield
|
||||
url: https://github.com/josefadamcik/SofleKeyboard
|
||||
requires: [pro_micro]
|
||||
exposes: [i2c_oled]
|
||||
features:
|
||||
- keys
|
||||
- display
|
||||
- encoder
|
||||
siblings:
|
||||
- sofle_left
|
||||
- sofle_right
|
11
app/boards/shields/splitreus62/splitreus62.zmk.yml
Normal file
11
app/boards/shields/splitreus62/splitreus62.zmk.yml
Normal file
|
@ -0,0 +1,11 @@
|
|||
file_format: "1"
|
||||
id: splitreus62
|
||||
name: Splitreus62
|
||||
type: shield
|
||||
url: https://github.com/Na-Cly/splitreus62
|
||||
requires: [pro_micro]
|
||||
features:
|
||||
- keys
|
||||
siblings:
|
||||
- splitreus62_left
|
||||
- splitreus62_right
|
8
app/boards/shields/tg4x/tg4x.zmk.yml
Normal file
8
app/boards/shields/tg4x/tg4x.zmk.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
file_format: "1"
|
||||
id: tg4x
|
||||
name: TG4x
|
||||
type: shield
|
||||
url: https://github.com/MythosMann/tg4x
|
||||
requires: [pro_micro]
|
||||
features:
|
||||
- keys
|
10
app/boards/shields/tidbit/tidbit.zmk.yml
Normal file
10
app/boards/shields/tidbit/tidbit.zmk.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
file_format: "1"
|
||||
id: tidbit
|
||||
name: Tidbit Numpad
|
||||
type: shield
|
||||
url: https://nullbits.co/tidbit/
|
||||
requires: [pro_micro]
|
||||
features:
|
||||
- keys
|
||||
- encoder
|
||||
- display
|
|
@ -1,5 +1,7 @@
|
|||
# Copyright (c) 2020 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
add_subdirectory(gpio)
|
||||
add_subdirectory(kscan)
|
||||
add_subdirectory(sensor)
|
||||
add_subdirectory(sensor)
|
||||
add_subdirectory(display)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# Copyright (c) 2020 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
rsource "gpio/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_ */
|
8
app/drivers/gpio/CMakeLists.txt
Normal file
8
app/drivers/gpio/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2020 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
zephyr_library_named(zmk__drivers__gpio)
|
||||
zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_GPIO_MCP23017 gpio_mcp23017.c)
|
||||
zephyr_library_sources_ifndef(CONFIG_GPIO_MCP23017 ${ZEPHYR_BASE}/misc/empty_file.c)
|
1
app/drivers/gpio/Kconfig
Normal file
1
app/drivers/gpio/Kconfig
Normal file
|
@ -0,0 +1 @@
|
|||
rsource "Kconfig.mcp23017"
|
21
app/drivers/gpio/Kconfig.mcp23017
Normal file
21
app/drivers/gpio/Kconfig.mcp23017
Normal file
|
@ -0,0 +1,21 @@
|
|||
# MCP23017 GPIO configuration options
|
||||
|
||||
# Copyright (c) 2021 Pete Johanson
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig GPIO_MCP23017
|
||||
bool "MCP23017 I2C-based GPIO chip"
|
||||
depends on I2C
|
||||
select HAS_DTS_GPIO
|
||||
help
|
||||
Enable driver for MCP23017 I2C-based GPIO chip.
|
||||
|
||||
if GPIO_MCP23017
|
||||
|
||||
config GPIO_MCP23017_INIT_PRIORITY
|
||||
int "Init priority"
|
||||
default 75
|
||||
help
|
||||
Device driver initialization priority.
|
||||
|
||||
endif #GPIO_MCP23017
|
332
app/drivers/gpio/gpio_mcp23017.c
Normal file
332
app/drivers/gpio/gpio_mcp23017.c
Normal file
|
@ -0,0 +1,332 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Geanix ApS
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT microchip_mcp23017
|
||||
|
||||
/**
|
||||
* @file Driver for MCP23017 SPI-based GPIO driver.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <kernel.h>
|
||||
#include <device.h>
|
||||
#include <init.h>
|
||||
#include <sys/byteorder.h>
|
||||
#include <drivers/gpio.h>
|
||||
#include <drivers/i2c.h>
|
||||
|
||||
#include "gpio_mcp23017.h"
|
||||
|
||||
#define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(gpio_mcp23017);
|
||||
|
||||
/**
|
||||
* @brief Read both port 0 and port 1 registers of certain register function.
|
||||
*
|
||||
* Given the register in reg, read the pair of port 0 and port 1.
|
||||
*
|
||||
* @param dev Device struct of the MCP23017.
|
||||
* @param reg Register to read (the PORTA of the pair of registers).
|
||||
* @param buf Buffer to read data into.
|
||||
*
|
||||
* @return 0 if successful, failed otherwise.
|
||||
*/
|
||||
static int read_port_regs(const struct device *dev, uint8_t reg, uint16_t *buf) {
|
||||
const struct mcp23017_config *const config = dev->config;
|
||||
struct mcp23017_drv_data *const drv_data = (struct mcp23017_drv_data *const)dev->data;
|
||||
int ret;
|
||||
uint16_t port_data;
|
||||
|
||||
uint8_t addr = config->slave;
|
||||
|
||||
ret = i2c_burst_read(drv_data->i2c, addr, reg, (uint8_t *)&port_data, sizeof(port_data));
|
||||
if (ret) {
|
||||
LOG_DBG("i2c_write_read FAIL %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*buf = sys_le16_to_cpu(port_data);
|
||||
|
||||
LOG_DBG("MCP23017: Read: REG[0x%X] = 0x%X, REG[0x%X] = 0x%X", reg, (*buf & 0xFF), (reg + 1),
|
||||
(*buf >> 8));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write both port 0 and port 1 registers of certain register function.
|
||||
*
|
||||
* Given the register in reg, write the pair of port 0 and port 1.
|
||||
*
|
||||
* @param dev Device struct of the MCP23017.
|
||||
* @param reg Register to write into (the PORTA of the pair of registers).
|
||||
* @param buf Buffer to write data from.
|
||||
*
|
||||
* @return 0 if successful, failed otherwise.
|
||||
*/
|
||||
static int write_port_regs(const struct device *dev, uint8_t reg, uint16_t value) {
|
||||
const struct mcp23017_config *const config = dev->config;
|
||||
struct mcp23017_drv_data *const drv_data = (struct mcp23017_drv_data *const)dev->data;
|
||||
int ret;
|
||||
uint16_t port_data;
|
||||
|
||||
LOG_DBG("MCP23017: Write: REG[0x%X] = 0x%X, REG[0x%X] = 0x%X", reg, (value & 0xFF), (reg + 1),
|
||||
(value >> 8));
|
||||
|
||||
port_data = sys_cpu_to_le16(value);
|
||||
|
||||
ret = i2c_burst_write(drv_data->i2c, config->slave, reg, (uint8_t *)&port_data,
|
||||
sizeof(port_data));
|
||||
if (ret) {
|
||||
LOG_DBG("i2c_write FAIL %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Setup the pin direction (input or output)
|
||||
*
|
||||
* @param dev Device struct of the MCP23017
|
||||
* @param pin The pin number
|
||||
* @param flags Flags of pin or port
|
||||
*
|
||||
* @return 0 if successful, failed otherwise
|
||||
*/
|
||||
static int setup_pin_dir(const struct device *dev, uint32_t pin, int flags) {
|
||||
struct mcp23017_drv_data *const drv_data = (struct mcp23017_drv_data *const)dev->data;
|
||||
uint16_t *dir = &drv_data->reg_cache.iodir;
|
||||
uint16_t *output = &drv_data->reg_cache.gpio;
|
||||
int ret;
|
||||
|
||||
if ((flags & GPIO_OUTPUT) != 0U) {
|
||||
if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0U) {
|
||||
*output |= BIT(pin);
|
||||
} else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0U) {
|
||||
*output &= ~BIT(pin);
|
||||
}
|
||||
*dir &= ~BIT(pin);
|
||||
} else {
|
||||
*dir |= BIT(pin);
|
||||
}
|
||||
|
||||
ret = write_port_regs(dev, REG_GPIO_PORTA, *output);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = write_port_regs(dev, REG_IODIR_PORTA, *dir);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Setup the pin pull up/pull down status
|
||||
*
|
||||
* @param dev Device struct of the MCP23017
|
||||
* @param pin The pin number
|
||||
* @param flags Flags of pin or port
|
||||
*
|
||||
* @return 0 if successful, failed otherwise
|
||||
*/
|
||||
static int setup_pin_pullupdown(const struct device *dev, uint32_t pin, int flags) {
|
||||
struct mcp23017_drv_data *const drv_data = (struct mcp23017_drv_data *const)dev->data;
|
||||
uint16_t port;
|
||||
int ret;
|
||||
|
||||
/* Setup pin pull up or pull down */
|
||||
port = drv_data->reg_cache.gppu;
|
||||
|
||||
/* pull down == 0, pull up == 1 */
|
||||
if ((flags & GPIO_PULL_DOWN) != 0U) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
WRITE_BIT(port, pin, (flags & GPIO_PULL_UP) != 0U);
|
||||
|
||||
ret = write_port_regs(dev, REG_GPPU_PORTA, port);
|
||||
if (ret == 0) {
|
||||
drv_data->reg_cache.gppu = port;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp23017_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) {
|
||||
struct mcp23017_drv_data *const drv_data = (struct mcp23017_drv_data *const)dev->data;
|
||||
int ret;
|
||||
|
||||
/* Can't do SPI bus operations from an ISR */
|
||||
if (k_is_in_isr()) {
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
k_sem_take(&drv_data->lock, K_FOREVER);
|
||||
|
||||
if ((flags & GPIO_OPEN_DRAIN) != 0U) {
|
||||
ret = -ENOTSUP;
|
||||
goto done;
|
||||
};
|
||||
|
||||
ret = setup_pin_dir(dev, pin, flags);
|
||||
if (ret) {
|
||||
LOG_ERR("MCP23017: error setting pin direction (%d)", ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = setup_pin_pullupdown(dev, pin, flags);
|
||||
if (ret) {
|
||||
LOG_ERR("MCP23017: error setting pin pull up/down (%d)", ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
k_sem_give(&drv_data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp23017_port_get_raw(const struct device *dev, uint32_t *value) {
|
||||
struct mcp23017_drv_data *const drv_data = (struct mcp23017_drv_data *const)dev->data;
|
||||
uint16_t buf;
|
||||
int ret;
|
||||
|
||||
/* Can't do SPI bus operations from an ISR */
|
||||
if (k_is_in_isr()) {
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
k_sem_take(&drv_data->lock, K_FOREVER);
|
||||
|
||||
ret = read_port_regs(dev, REG_GPIO_PORTA, &buf);
|
||||
if (ret != 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
*value = buf;
|
||||
|
||||
done:
|
||||
k_sem_give(&drv_data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp23017_port_set_masked_raw(const struct device *dev, uint32_t mask, uint32_t value) {
|
||||
struct mcp23017_drv_data *const drv_data = (struct mcp23017_drv_data *const)dev->data;
|
||||
uint16_t buf;
|
||||
int ret;
|
||||
|
||||
/* Can't do SPI bus operations from an ISR */
|
||||
if (k_is_in_isr()) {
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
k_sem_take(&drv_data->lock, K_FOREVER);
|
||||
|
||||
buf = drv_data->reg_cache.gpio;
|
||||
buf = (buf & ~mask) | (mask & value);
|
||||
|
||||
ret = write_port_regs(dev, REG_GPIO_PORTA, buf);
|
||||
if (ret == 0) {
|
||||
drv_data->reg_cache.gpio = buf;
|
||||
}
|
||||
|
||||
k_sem_give(&drv_data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp23017_port_set_bits_raw(const struct device *dev, uint32_t mask) {
|
||||
return mcp23017_port_set_masked_raw(dev, mask, mask);
|
||||
}
|
||||
|
||||
static int mcp23017_port_clear_bits_raw(const struct device *dev, uint32_t mask) {
|
||||
return mcp23017_port_set_masked_raw(dev, mask, 0);
|
||||
}
|
||||
|
||||
static int mcp23017_port_toggle_bits(const struct device *dev, uint32_t mask) {
|
||||
struct mcp23017_drv_data *const drv_data = (struct mcp23017_drv_data *const)dev->data;
|
||||
uint16_t buf;
|
||||
int ret;
|
||||
|
||||
/* Can't do SPI bus operations from an ISR */
|
||||
if (k_is_in_isr()) {
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
k_sem_take(&drv_data->lock, K_FOREVER);
|
||||
|
||||
buf = drv_data->reg_cache.gpio;
|
||||
buf ^= mask;
|
||||
|
||||
ret = write_port_regs(dev, REG_GPIO_PORTA, buf);
|
||||
if (ret == 0) {
|
||||
drv_data->reg_cache.gpio = buf;
|
||||
}
|
||||
|
||||
k_sem_give(&drv_data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp23017_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
|
||||
enum gpio_int_mode mode, enum gpio_int_trig trig) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static const struct gpio_driver_api api_table = {
|
||||
.pin_configure = mcp23017_config,
|
||||
.port_get_raw = mcp23017_port_get_raw,
|
||||
.port_set_masked_raw = mcp23017_port_set_masked_raw,
|
||||
.port_set_bits_raw = mcp23017_port_set_bits_raw,
|
||||
.port_clear_bits_raw = mcp23017_port_clear_bits_raw,
|
||||
.port_toggle_bits = mcp23017_port_toggle_bits,
|
||||
.pin_interrupt_configure = mcp23017_pin_interrupt_configure,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Initialization function of MCP23017
|
||||
*
|
||||
* @param dev Device struct
|
||||
* @return 0 if successful, failed otherwise.
|
||||
*/
|
||||
static int mcp23017_init(const struct device *dev) {
|
||||
const struct mcp23017_config *const config = dev->config;
|
||||
struct mcp23017_drv_data *const drv_data = (struct mcp23017_drv_data *const)dev->data;
|
||||
|
||||
drv_data->i2c = device_get_binding((char *)config->i2c_dev_name);
|
||||
if (!drv_data->i2c) {
|
||||
LOG_DBG("Unable to get i2c device");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
k_sem_init(&drv_data->lock, 1, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MCP23017_INIT(inst) \
|
||||
static struct mcp23017_config mcp23017_##inst##_config = { \
|
||||
.i2c_dev_name = DT_INST_BUS_LABEL(inst), \
|
||||
.slave = DT_INST_REG_ADDR(inst), \
|
||||
\
|
||||
}; \
|
||||
\
|
||||
static struct mcp23017_drv_data mcp23017_##inst##_drvdata = { \
|
||||
/* Default for registers according to datasheet */ \
|
||||
.reg_cache.iodir = 0xFFFF, .reg_cache.ipol = 0x0, .reg_cache.gpinten = 0x0, \
|
||||
.reg_cache.defval = 0x0, .reg_cache.intcon = 0x0, .reg_cache.iocon = 0x0, \
|
||||
.reg_cache.gppu = 0x0, .reg_cache.intf = 0x0, .reg_cache.intcap = 0x0, \
|
||||
.reg_cache.gpio = 0x0, .reg_cache.olat = 0x0, \
|
||||
}; \
|
||||
\
|
||||
/* This has to init after SPI master */ \
|
||||
DEVICE_DT_INST_DEFINE(inst, mcp23017_init, device_pm_control_nop, &mcp23017_##inst##_drvdata, \
|
||||
&mcp23017_##inst##_config, POST_KERNEL, \
|
||||
CONFIG_GPIO_MCP23017_INIT_PRIORITY, &api_table);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(MCP23017_INIT)
|
86
app/drivers/gpio/gpio_mcp23017.h
Normal file
86
app/drivers/gpio/gpio_mcp23017.h
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Geanix ApS, Pete Johanson
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file Header file for the MCP23017 driver.
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_GPIO_GPIO_MCP23017_H_
|
||||
#define ZEPHYR_DRIVERS_GPIO_GPIO_MCP23017_H_
|
||||
|
||||
#include <kernel.h>
|
||||
|
||||
#include <drivers/gpio.h>
|
||||
#include <drivers/i2c.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Register definitions */
|
||||
#define REG_IODIR_PORTA 0x00
|
||||
#define REG_IODIR_PORTB 0x01
|
||||
#define REG_IPOL_PORTA 0x02
|
||||
#define REG_IPOL_PORTB 0x03
|
||||
#define REG_GPINTEN_PORTA 0x04
|
||||
#define REG_GPINTEN_PORTB 0x05
|
||||
#define REG_DEFVAL_PORTA 0x06
|
||||
#define REG_DEFVAL_PORTB 0x07
|
||||
#define REG_INTCON_PORTA 0x08
|
||||
#define REG_INTCON_PORTB 0x09
|
||||
#define REG_GPPU_PORTA 0x0C
|
||||
#define REG_GPPU_PORTB 0x0D
|
||||
#define REG_INTF_PORTA 0x0E
|
||||
#define REG_INTF_PORTB 0x0F
|
||||
#define REG_INTCAP_PORTA 0x10
|
||||
#define REG_INTCAP_PORTB 0x11
|
||||
#define REG_GPIO_PORTA 0x12
|
||||
#define REG_GPIO_PORTB 0x13
|
||||
#define REG_OLAT_PORTA 0x14
|
||||
#define REG_OLAT_PORTB 0x15
|
||||
|
||||
#define MCP23017_ADDR 0x40
|
||||
#define MCP23017_READBIT 0x01
|
||||
|
||||
/** Configuration data */
|
||||
struct mcp23017_config {
|
||||
/* gpio_driver_data needs to be first */
|
||||
struct gpio_driver_config common;
|
||||
|
||||
const char *const i2c_dev_name;
|
||||
const uint16_t slave;
|
||||
};
|
||||
|
||||
/** Runtime driver data */
|
||||
struct mcp23017_drv_data {
|
||||
/* gpio_driver_data needs to be first */
|
||||
struct gpio_driver_config data;
|
||||
|
||||
/** Master SPI device */
|
||||
const struct device *i2c;
|
||||
|
||||
struct k_sem lock;
|
||||
|
||||
struct {
|
||||
uint16_t iodir;
|
||||
uint16_t ipol;
|
||||
uint16_t gpinten;
|
||||
uint16_t defval;
|
||||
uint16_t intcon;
|
||||
uint16_t iocon;
|
||||
uint16_t gppu;
|
||||
uint16_t intf;
|
||||
uint16_t intcap;
|
||||
uint16_t gpio;
|
||||
uint16_t olat;
|
||||
} reg_cache;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_GPIO_GPIO_MCP23017_H_ */
|
|
@ -41,7 +41,12 @@ static int kscan_composite_enable_callback(const struct device *dev) {
|
|||
for (int i = 0; i < ARRAY_SIZE(kscan_composite_children); i++) {
|
||||
const struct kscan_composite_child_config *cfg = &kscan_composite_children[i];
|
||||
|
||||
kscan_enable_callback(device_get_binding(cfg->label));
|
||||
const struct device *dev = device_get_binding(cfg->label);
|
||||
if (!dev) {
|
||||
LOG_WRN("Failed to load child kscan device %s", log_strdup(cfg->label));
|
||||
continue;
|
||||
}
|
||||
kscan_enable_callback(dev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -50,7 +55,12 @@ static int kscan_composite_disable_callback(const struct device *dev) {
|
|||
for (int i = 0; i < ARRAY_SIZE(kscan_composite_children); i++) {
|
||||
const struct kscan_composite_child_config *cfg = &kscan_composite_children[i];
|
||||
|
||||
kscan_disable_callback(device_get_binding(cfg->label));
|
||||
const struct device *dev = device_get_binding(cfg->label);
|
||||
if (!dev) {
|
||||
LOG_WRN("Failed to load child kscan device %s", log_strdup(cfg->label));
|
||||
continue;
|
||||
}
|
||||
kscan_disable_callback(dev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,48 +1,158 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
* Copyright (c) 2020-2021 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_kscan_gpio_matrix
|
||||
|
||||
#include <device.h>
|
||||
#include <drivers/kscan.h>
|
||||
#include <devicetree.h>
|
||||
#include <drivers/gpio.h>
|
||||
#include <drivers/kscan.h>
|
||||
#include <logging/log.h>
|
||||
#include <sys/__assert.h>
|
||||
#include <sys/util.h>
|
||||
|
||||
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)
|
||||
|
||||
struct kscan_gpio_item_config {
|
||||
char *label;
|
||||
#define INST_DIODE_DIR(n) DT_ENUM_IDX(DT_DRV_INST(n), diode_direction)
|
||||
#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_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), \
|
||||
.pin = DT_INST_GPIO_PIN_BY_IDX(n, prop, idx), \
|
||||
.flags = DT_INST_GPIO_FLAGS_BY_IDX(n, prop, idx), \
|
||||
},
|
||||
.port = DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(node_id, prop, idx)), \
|
||||
.pin = DT_GPIO_PIN_BY_IDX(node_id, 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_COL_CFG_INIT(idx, n) _KSCAN_GPIO_ITEM_CFG_INIT(n, col_gpios, idx)
|
||||
#define KSCAN_GPIO_ROW_CFG_INIT(idx, inst_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)
|
||||
static int kscan_gpio_config_interrupts(const struct device **devices,
|
||||
const struct kscan_gpio_item_config *configs, size_t len,
|
||||
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];
|
||||
enum kscan_diode_direction {
|
||||
KSCAN_ROW2COL,
|
||||
KSCAN_COL2ROW,
|
||||
};
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -51,257 +161,299 @@ static int kscan_gpio_config_interrupts(const struct device **devices,
|
|||
}
|
||||
#endif
|
||||
|
||||
#define COND_POLLING(code) COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (code), ())
|
||||
#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)
|
||||
#if USE_INTERRUPTS
|
||||
static int kscan_matrix_interrupt_enable(const struct device *dev) {
|
||||
int err = kscan_matrix_interrupt_configure(dev, GPIO_INT_LEVEL_ACTIVE);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
#define INST_MATRIX_ROWS(n) DT_INST_PROP_LEN(n, row_gpios)
|
||||
#define INST_MATRIX_COLS(n) DT_INST_PROP_LEN(n, col_gpios)
|
||||
#define INST_OUTPUT_LEN(n) \
|
||||
COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (INST_MATRIX_ROWS(n)), \
|
||||
(INST_MATRIX_COLS(n)))
|
||||
#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)))
|
||||
// While interrupts are enabled, set all outputs active so a pressed key
|
||||
// will trigger an interrupt.
|
||||
return kscan_matrix_set_all_outputs(dev, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define GPIO_INST_INIT(n) \
|
||||
COND_INTERRUPTS( \
|
||||
struct kscan_gpio_irq_callback_##n { \
|
||||
struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) * \
|
||||
work; \
|
||||
struct gpio_callback callback; \
|
||||
const struct device *dev; \
|
||||
}; \
|
||||
static struct kscan_gpio_irq_callback_##n irq_callbacks_##n[INST_INPUT_LEN(n)];) \
|
||||
struct kscan_gpio_config_##n { \
|
||||
struct kscan_gpio_item_config rows[INST_MATRIX_ROWS(n)]; \
|
||||
struct kscan_gpio_item_config cols[INST_MATRIX_COLS(n)]; \
|
||||
}; \
|
||||
struct kscan_gpio_data_##n { \
|
||||
kscan_callback_t callback; \
|
||||
COND_POLLING(struct k_timer poll_timer;) \
|
||||
struct COND_CODE_0(DT_INST_PROP(n, debounce_period), (k_work), (k_delayed_work)) work; \
|
||||
bool matrix_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \
|
||||
const struct device *rows[INST_MATRIX_ROWS(n)]; \
|
||||
const struct device *cols[INST_MATRIX_COLS(n)]; \
|
||||
const struct device *dev; \
|
||||
}; \
|
||||
static const struct device **kscan_gpio_input_devices_##n(const struct device *dev) { \
|
||||
struct kscan_gpio_data_##n *data = dev->data; \
|
||||
return (COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (data->cols), \
|
||||
(data->rows))); \
|
||||
} \
|
||||
static const struct kscan_gpio_item_config *kscan_gpio_input_configs_##n( \
|
||||
const struct device *dev) { \
|
||||
const struct kscan_gpio_config_##n *cfg = dev->config; \
|
||||
return (( \
|
||||
COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (cfg->cols), (cfg->rows)))); \
|
||||
} \
|
||||
static const struct device **kscan_gpio_output_devices_##n(const struct device *dev) { \
|
||||
struct kscan_gpio_data_##n *data = dev->data; \
|
||||
return (COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (data->rows), \
|
||||
(data->cols))); \
|
||||
} \
|
||||
static const struct kscan_gpio_item_config *kscan_gpio_output_configs_##n( \
|
||||
const struct device *dev) { \
|
||||
const struct kscan_gpio_config_##n *cfg = dev->config; \
|
||||
return ( \
|
||||
COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (cfg->rows), (cfg->cols))); \
|
||||
} \
|
||||
COND_INTERRUPTS( \
|
||||
static int kscan_gpio_enable_interrupts_##n(const struct device *dev) { \
|
||||
return kscan_gpio_config_interrupts(kscan_gpio_input_devices_##n(dev), \
|
||||
kscan_gpio_input_configs_##n(dev), \
|
||||
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), \
|
||||
kscan_gpio_input_configs_##n(dev), \
|
||||
INST_INPUT_LEN(n), GPIO_INT_DISABLE); \
|
||||
}) \
|
||||
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]; \
|
||||
const struct kscan_gpio_item_config *cfg = &kscan_gpio_output_configs_##n(dev)[i]; \
|
||||
if ((err = gpio_pin_set(in_dev, cfg->pin, value))) { \
|
||||
LOG_DBG("FAILED TO SET OUTPUT %d to %d", cfg->pin, err); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
static void kscan_gpio_set_matrix_state_##n( \
|
||||
bool state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)], uint32_t input_index, \
|
||||
uint32_t output_index, bool value) { \
|
||||
state[COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (output_index), \
|
||||
(input_index))] \
|
||||
[COND_CODE_0(DT_ENUM_IDX(DT_DRV_INST(n), diode_direction), (input_index), \
|
||||
(output_index))] = value; \
|
||||
} \
|
||||
static int kscan_gpio_read_##n(const struct device *dev) { \
|
||||
COND_INTERRUPTS(bool submit_follow_up_read = false;) \
|
||||
struct kscan_gpio_data_##n *data = dev->data; \
|
||||
static bool read_state[INST_MATRIX_ROWS(n)][INST_MATRIX_COLS(n)]; \
|
||||
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);) \
|
||||
for (int o = 0; o < INST_OUTPUT_LEN(n); o++) { \
|
||||
const struct device *out_dev = kscan_gpio_output_devices_##n(dev)[o]; \
|
||||
const struct kscan_gpio_item_config *out_cfg = &kscan_gpio_output_configs_##n(dev)[o]; \
|
||||
err = gpio_pin_set(out_dev, out_cfg->pin, 1); \
|
||||
if (err) { \
|
||||
LOG_ERR("Failed to set output active (err %d)", err); \
|
||||
return err; \
|
||||
} \
|
||||
for (int i = 0; i < INST_INPUT_LEN(n); i++) { \
|
||||
const struct device *in_dev = kscan_gpio_input_devices_##n(dev)[i]; \
|
||||
const struct kscan_gpio_item_config *in_cfg = \
|
||||
&kscan_gpio_input_configs_##n(dev)[i]; \
|
||||
kscan_gpio_set_matrix_state_##n(read_state, i, o, \
|
||||
gpio_pin_get(in_dev, in_cfg->pin) > 0); \
|
||||
} \
|
||||
err = gpio_pin_set(out_dev, out_cfg->pin, 0); \
|
||||
if (err) { \
|
||||
LOG_ERR("Failed to set output inactive (err %d)", err); \
|
||||
return err; \
|
||||
} \
|
||||
} \
|
||||
/* Set all our outputs as active again. */ \
|
||||
COND_INTERRUPTS(kscan_gpio_set_output_state_##n(dev, 1);) \
|
||||
for (int r = 0; r < INST_MATRIX_ROWS(n); r++) { \
|
||||
for (int c = 0; c < INST_MATRIX_COLS(n); c++) { \
|
||||
bool pressed = read_state[r][c]; \
|
||||
/* Follow up reads needed because further interrupts won't fire on already tripped \
|
||||
* input GPIO pins */ \
|
||||
COND_INTERRUPTS(submit_follow_up_read = (submit_follow_up_read || pressed);) \
|
||||
if (pressed != data->matrix_state[r][c]) { \
|
||||
LOG_DBG("Sending event at %d,%d state %s", r, c, (pressed ? "on" : "off")); \
|
||||
data->matrix_state[r][c] = pressed; \
|
||||
data->callback(dev, r, c, pressed); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
COND_INTERRUPTS( \
|
||||
if (submit_follow_up_read) { \
|
||||
COND_CODE_0(DT_INST_PROP(n, debounce_period), ({ k_work_submit(&data->work); }), \
|
||||
({ \
|
||||
k_delayed_work_cancel(&data->work); \
|
||||
k_delayed_work_submit(&data->work, K_MSEC(5)); \
|
||||
})) \
|
||||
} else { kscan_gpio_enable_interrupts_##n(dev); }) \
|
||||
return 0; \
|
||||
} \
|
||||
static void kscan_gpio_work_handler_##n(struct k_work *work) { \
|
||||
struct kscan_gpio_data_##n *data = CONTAINER_OF(work, struct kscan_gpio_data_##n, work); \
|
||||
kscan_gpio_read_##n(data->dev); \
|
||||
} \
|
||||
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 = \
|
||||
CONTAINER_OF(cb, struct kscan_gpio_irq_callback_##n, callback); \
|
||||
kscan_gpio_disable_interrupts_##n(data->dev); \
|
||||
COND_CODE_0(DT_INST_PROP(n, debounce_period), ({ k_work_submit(data->work); }), ({ \
|
||||
k_delayed_work_cancel(data->work); \
|
||||
k_delayed_work_submit(data->work, \
|
||||
K_MSEC(DT_INST_PROP(n, debounce_period))); \
|
||||
})) \
|
||||
}) \
|
||||
#if USE_INTERRUPTS
|
||||
static int kscan_matrix_interrupt_disable(const struct device *dev) {
|
||||
int err = kscan_matrix_interrupt_configure(dev, GPIO_INT_DISABLE);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// While interrupts are disabled, set all outputs inactive so
|
||||
// kscan_matrix_read() can scan them one by one.
|
||||
return kscan_matrix_set_all_outputs(dev, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_INTERRUPTS
|
||||
static void kscan_matrix_irq_callback_handler(const struct device *port, struct gpio_callback *cb,
|
||||
const gpio_port_pins_t pin) {
|
||||
struct kscan_matrix_irq_callback *data =
|
||||
CONTAINER_OF(cb, struct kscan_matrix_irq_callback, callback);
|
||||
const struct kscan_matrix_config *config = data->dev->config;
|
||||
|
||||
// Disable our interrupts temporarily to avoid re-entry while we scan.
|
||||
kscan_matrix_interrupt_disable(data->dev);
|
||||
|
||||
// TODO (Zephyr 2.6): use k_work_reschedule()
|
||||
k_delayed_work_cancel(data->work);
|
||||
k_delayed_work_submit(data->work, K_MSEC(config->debounce_period_ms));
|
||||
}
|
||||
#endif
|
||||
|
||||
static int kscan_matrix_read(const struct device *dev) {
|
||||
struct kscan_matrix_data *data = dev->data;
|
||||
const struct kscan_matrix_config *config = dev->config;
|
||||
|
||||
// Scan the matrix.
|
||||
for (int o = 0; o < config->outputs.len; o++) {
|
||||
const struct kscan_gpio_dt_spec *out_gpio = &config->outputs.gpios[o];
|
||||
|
||||
int err = gpio_pin_set(out_gpio->port, out_gpio->pin, 1);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to set output %i active: %i", o, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
for (int i = 0; i < config->inputs.len; i++) {
|
||||
const struct kscan_gpio_dt_spec *in_gpio = &config->inputs.gpios[i];
|
||||
|
||||
const int index = state_index_io(config, i, o);
|
||||
data->next_state[index] = gpio_pin_get(in_gpio->port, in_gpio->pin);
|
||||
}
|
||||
|
||||
err = gpio_pin_set(out_gpio->port, out_gpio->pin, 0);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to set output %i inactive: %i", o, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// Process the new state.
|
||||
#if USE_INTERRUPTS
|
||||
bool submit_followup_read = false;
|
||||
#endif
|
||||
|
||||
for (int r = 0; r < config->rows.len; r++) {
|
||||
for (int c = 0; c < config->cols.len; c++) {
|
||||
const int index = state_index_rc(config, r, c);
|
||||
const bool pressed = data->next_state[index];
|
||||
|
||||
// Follow up reads are needed if any key is pressed because further
|
||||
// interrupts won't fire on already tripped GPIO pins.
|
||||
#if USE_INTERRUPTS
|
||||
submit_followup_read = submit_followup_read || pressed;
|
||||
#endif
|
||||
if (pressed != data->current_state[index]) {
|
||||
LOG_DBG("Sending event at %i,%i state %s", r, c, pressed ? "on" : "off");
|
||||
data->current_state[index] = pressed;
|
||||
data->callback(dev, r, c, pressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_INTERRUPTS
|
||||
if (submit_followup_read) {
|
||||
// At least one key is pressed. Poll until everything is released.
|
||||
// TODO (Zephyr 2.6): use k_work_reschedule()
|
||||
k_delayed_work_cancel(&data->work);
|
||||
k_delayed_work_submit(&data->work, K_MSEC(config->debounce_period_ms));
|
||||
} else {
|
||||
// All keys are released. Return to waiting for an interrupt.
|
||||
kscan_matrix_interrupt_enable(dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if USE_POLLING
|
||||
static void kscan_matrix_timer_handler(struct k_timer *timer) {
|
||||
struct kscan_matrix_data *data = CONTAINER_OF(timer, struct kscan_matrix_data, poll_timer);
|
||||
k_delayed_work_submit(&data->work, K_NO_WAIT);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void kscan_matrix_work_handler(struct k_work *work) {
|
||||
struct k_delayed_work *dwork = CONTAINER_OF(work, struct k_delayed_work, work);
|
||||
struct kscan_matrix_data *data = CONTAINER_OF(dwork, struct kscan_matrix_data, work);
|
||||
kscan_matrix_read(data->dev);
|
||||
}
|
||||
|
||||
static int kscan_matrix_configure(const struct device *dev, const kscan_callback_t callback) {
|
||||
struct kscan_matrix_data *data = dev->data;
|
||||
|
||||
if (!callback) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data->callback = callback;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kscan_matrix_enable(const struct device *dev) {
|
||||
#if USE_POLLING
|
||||
struct kscan_matrix_data *data = dev->data;
|
||||
const struct kscan_matrix_config *config = dev->config;
|
||||
|
||||
k_timer_start(&data->poll_timer, K_MSEC(config->poll_period_ms),
|
||||
K_MSEC(config->poll_period_ms));
|
||||
return 0;
|
||||
#else
|
||||
// Read will automatically enable interrupts once done.
|
||||
return kscan_matrix_read(dev);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int kscan_matrix_disable(const struct device *dev) {
|
||||
#if USE_POLLING
|
||||
struct kscan_matrix_data *data = dev->data;
|
||||
|
||||
k_timer_stop(&data->poll_timer);
|
||||
return 0;
|
||||
#else
|
||||
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 = { \
|
||||
.rows = {[INST_MATRIX_ROWS(n) - 1] = NULL}, .cols = {[INST_MATRIX_COLS(n) - 1] = NULL}}; \
|
||||
static int kscan_gpio_configure_##n(const struct device *dev, kscan_callback_t callback) { \
|
||||
struct kscan_gpio_data_##n *data = dev->data; \
|
||||
if (!callback) { \
|
||||
return -EINVAL; \
|
||||
} \
|
||||
data->callback = callback; \
|
||||
LOG_DBG("Configured GPIO %d", n); \
|
||||
return 0; \
|
||||
static const struct kscan_gpio_dt_spec kscan_matrix_cols_##index[] = { \
|
||||
UTIL_LISTIFY(INST_COLS_LEN(index), KSCAN_GPIO_COL_CFG_INIT, index)}; \
|
||||
\
|
||||
static bool kscan_current_state_##index[INST_MATRIX_LEN(index)]; \
|
||||
static bool kscan_next_state_##index[INST_MATRIX_LEN(index)]; \
|
||||
\
|
||||
COND_INTERRUPTS((static struct kscan_matrix_irq_callback \
|
||||
kscan_matrix_irqs_##index[INST_INPUTS_LEN(index)];)) \
|
||||
\
|
||||
static struct kscan_matrix_data kscan_matrix_data_##index = { \
|
||||
.current_state = kscan_current_state_##index, \
|
||||
.next_state = kscan_next_state_##index, \
|
||||
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; \
|
||||
k_timer_start(&data->poll_timer, K_MSEC(10), K_MSEC(10)); \
|
||||
return 0;), \
|
||||
(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_DT_INST_DEFINE(n, kscan_gpio_init_##n, device_pm_control_nop, &kscan_gpio_data_##n, \
|
||||
&kscan_gpio_config_##n, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY, \
|
||||
&gpio_driver_api_##n);
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(index, &kscan_matrix_init, device_pm_control_nop, \
|
||||
&kscan_matrix_data_##index, &kscan_matrix_config_##index, APPLICATION, \
|
||||
CONFIG_APPLICATION_INIT_PRIORITY, &kscan_matrix_api);
|
||||
|
||||
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)
|
||||
|
|
29
app/drivers/zephyr/dts/bindings/gpio/microchip,mcp23017.yaml
Normal file
29
app/drivers/zephyr/dts/bindings/gpio/microchip,mcp23017.yaml
Normal file
|
@ -0,0 +1,29 @@
|
|||
#
|
||||
# Copyright (c) 2020 Geanix ApS
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
description: >
|
||||
This is a representation of the Microchip MCP23017 I2C Gpio Expander.
|
||||
|
||||
compatible: "microchip,mcp23017"
|
||||
|
||||
include: [gpio-controller.yaml, i2c-device.yaml]
|
||||
|
||||
properties:
|
||||
label:
|
||||
required: true
|
||||
|
||||
"#gpio-cells":
|
||||
const: 2
|
||||
|
||||
ngpios:
|
||||
type: int
|
||||
required: true
|
||||
const: 16
|
||||
description: Number of gpios supported
|
||||
|
||||
gpio-cells:
|
||||
- pin
|
||||
- flags
|
|
@ -17,6 +17,11 @@ properties:
|
|||
debounce-period:
|
||||
type: int
|
||||
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:
|
||||
type: string
|
||||
default: row2col
|
||||
|
|
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
|
|
@ -4,6 +4,57 @@
|
|||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/** @file display.h
|
||||
* @brief Display functions and macros.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
int zmk_display_init();
|
||||
struct k_work_q *zmk_display_work_q();
|
||||
|
||||
bool zmk_display_is_initialized();
|
||||
int zmk_display_init();
|
||||
|
||||
/**
|
||||
* @brief Macro to define a ZMK event listener that handles the thread safety of fetching
|
||||
* the necessary state from the system work queue context, invoking a work callback
|
||||
* in the display queue context, and properly accessing that state safely when performing
|
||||
* display/LVGL updates.
|
||||
*
|
||||
* @param listener THe ZMK Event manager listener name.
|
||||
* @param state_type The struct/enum type used to store/transfer state.
|
||||
* @param cb The callback to invoke in the dispaly queue context to update the UI. Should be `void
|
||||
* func(state_type)` signature.
|
||||
* @param state_func The callback function to invoke to fetch the updated state from ZMK core.
|
||||
* Should be `state type func(const zmk_event_t *eh)` signature.
|
||||
* @retval listner##_init Generates a function `listener##_init` that should be called by the widget
|
||||
* once ready to be updated.
|
||||
**/
|
||||
#define ZMK_DISPLAY_WIDGET_LISTENER(listener, state_type, cb, state_func) \
|
||||
K_MUTEX_DEFINE(listener##_mutex); \
|
||||
static state_type __##listener##_state; \
|
||||
static state_type listener##_get_local_state() { \
|
||||
k_mutex_lock(&listener##_mutex, K_FOREVER); \
|
||||
state_type copy = __##listener##_state; \
|
||||
k_mutex_unlock(&listener##_mutex); \
|
||||
return copy; \
|
||||
}; \
|
||||
static void listener##_work_cb(struct k_work *work) { cb(listener##_get_local_state()); }; \
|
||||
K_WORK_DEFINE(listener##_work, listener##_work_cb); \
|
||||
static void listener##_refresh_state(const zmk_event_t *eh) { \
|
||||
k_mutex_lock(&listener##_mutex, K_FOREVER); \
|
||||
__##listener##_state = state_func(eh); \
|
||||
k_mutex_unlock(&listener##_mutex); \
|
||||
}; \
|
||||
static void listener##_init() { \
|
||||
listener##_refresh_state(NULL); \
|
||||
listener##_work_cb(NULL); \
|
||||
} \
|
||||
static int listener##_cb(const zmk_event_t *eh) { \
|
||||
if (zmk_display_is_initialized()) { \
|
||||
listener##_refresh_state(eh); \
|
||||
k_work_submit_to_queue(zmk_display_work_q(), &listener##_work); \
|
||||
} \
|
||||
return ZMK_EV_EVENT_BUBBLE; \
|
||||
} \
|
||||
ZMK_LISTENER(listener, listener##_cb);
|
||||
|
|
|
@ -6,13 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <zmk/keys.h>
|
||||
#include <zmk/hid.h>
|
||||
|
||||
enum zmk_endpoint {
|
||||
ZMK_ENDPOINT_USB,
|
||||
ZMK_ENDPOINT_BLE,
|
||||
};
|
||||
#include <zmk/endpoints_types.h>
|
||||
|
||||
int zmk_endpoints_select(enum zmk_endpoint endpoint);
|
||||
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);
|
36
app/package-lock.json
generated
Normal file
36
app/package-lock.json
generated
Normal file
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"name": "zmkfirmware",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "zmkfirmware",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"prettier": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.0.tgz",
|
||||
"integrity": "sha512-DsEPLY1dE5HF3BxCRBmD4uYZ+5DCbvatnolqTqcxEgKVZnL2kUfyu7b8pPQ5+hTBkdhU9SLUmK0/pHb07RE4WQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"prettier": "bin-prettier.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"prettier": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.0.tgz",
|
||||
"integrity": "sha512-DsEPLY1dE5HF3BxCRBmD4uYZ+5DCbvatnolqTqcxEgKVZnL2kUfyu7b8pPQ5+hTBkdhU9SLUmK0/pHb07RE4WQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
23
app/package.json
Normal file
23
app/package.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "zmkfirmware",
|
||||
"version": "1.0.0",
|
||||
"description": "ZMK Firmware tooling",
|
||||
"private": "true",
|
||||
"scripts": {
|
||||
"prettier:check": "prettier --check boards/**/*.yml",
|
||||
"prettier:format": "prettier --write boards/**/*.yml"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/zmkfirware/zmk.git"
|
||||
},
|
||||
"author": "ZMK Contributors",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/zmkfirware/zmk/issues"
|
||||
},
|
||||
"homepage": "https://zmk.dev/",
|
||||
"devDependencies": {
|
||||
"prettier": "^2.4.0"
|
||||
}
|
||||
}
|
8
app/scripts/requirements.txt
Normal file
8
app/scripts/requirements.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2021 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
# Convert YAML to JSON for validation
|
||||
remarshal>=0.14.0
|
||||
|
||||
# Perform our hardware metadata validation
|
||||
jsonschema>=3.2.0
|
|
@ -7,3 +7,8 @@ west-commands:
|
|||
- name: test
|
||||
class: Test
|
||||
help: run ZMK testsuite
|
||||
- file: scripts/west_commands/metadata.py
|
||||
commands:
|
||||
- name: metadata
|
||||
class: Metadata
|
||||
help: Operate on ZMK metadata files
|
||||
|
|
59
app/scripts/west_commands/metadata.py
Normal file
59
app/scripts/west_commands/metadata.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
# Copyright (c) 2021 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
'''Metadata command for ZMK.'''
|
||||
|
||||
from functools import cached_property
|
||||
import glob
|
||||
import json
|
||||
from jsonschema import validate, ValidationError
|
||||
import os
|
||||
import sys
|
||||
import yaml
|
||||
from textwrap import dedent # just for nicer code indentation
|
||||
|
||||
from west.commands import WestCommand
|
||||
from west import log # use this for user output
|
||||
|
||||
|
||||
class Metadata(WestCommand):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
'metadata', # gets stored as self.name
|
||||
'ZMK hardware metadata commands', # self.help
|
||||
# self.description:
|
||||
dedent('''Operate on the board/shield metadata.'''))
|
||||
|
||||
def do_add_parser(self, parser_adder):
|
||||
parser = parser_adder.add_parser(self.name,
|
||||
help=self.help,
|
||||
description=self.description)
|
||||
|
||||
parser.add_argument('subcommand', default="check",
|
||||
help='The subcommand to run. Defaults to "check".', nargs="?")
|
||||
return parser # gets stored as self.parser
|
||||
|
||||
@cached_property
|
||||
def schema(self):
|
||||
return json.load(
|
||||
open("../schema/hardware-metadata.schema.json", 'r'))
|
||||
|
||||
def validate_file(self, file):
|
||||
print("Validating: " + file)
|
||||
with open(file, 'r') as stream:
|
||||
try:
|
||||
validate(yaml.safe_load(stream), self.schema)
|
||||
except yaml.YAMLError as exc:
|
||||
print("Failed loading metadata yaml: " + file)
|
||||
print(exc)
|
||||
return False
|
||||
except ValidationError as vexc:
|
||||
print("Failed validation of: " + file)
|
||||
print(vexc)
|
||||
return False
|
||||
return True
|
||||
|
||||
def do_run(self, args, unknown_args):
|
||||
status = all([self.validate_file(f) for f in glob.glob(
|
||||
"boards/**/*.zmk.yml", recursive=True)])
|
||||
|
||||
sys.exit(0 if status else 1)
|
|
@ -7,6 +7,7 @@
|
|||
#include <device.h>
|
||||
#include <init.h>
|
||||
#include <kernel.h>
|
||||
#include <power/power.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;
|
||||
#if IS_ENABLED(CONFIG_ZMK_SLEEP)
|
||||
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);
|
||||
} else
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_SLEEP) */
|
||||
|
|
|
@ -29,6 +29,29 @@ config ZMK_DISPLAY_STATUS_SCREEN_CUSTOM
|
|||
|
||||
endchoice
|
||||
|
||||
choice ZMK_DISPLAY_WORK_QUEUE
|
||||
prompt "Work queue selection for UI updates"
|
||||
|
||||
config ZMK_DISPLAY_WORK_QUEUE_SYSTEM
|
||||
bool "Use default system work queue for UI updates"
|
||||
|
||||
config ZMK_DISPLAY_WORK_QUEUE_DEDICATED
|
||||
bool "Use dedicated work queue for UI updates"
|
||||
|
||||
endchoice
|
||||
|
||||
if ZMK_DISPLAY_WORK_QUEUE_DEDICATED
|
||||
|
||||
config ZMK_DISPLAY_DEDICATED_THREAD_STACK_SIZE
|
||||
int "Stack size for dedicated UI thread/queue"
|
||||
default 2048
|
||||
|
||||
config ZMK_DISPLAY_DEDICATED_THREAD_PRIORITY
|
||||
int "Thread priority for dedicated UI thread/queue"
|
||||
default 5
|
||||
|
||||
endif # ZMK_DISPLAY_WORK_QUEUE_DEDICATED
|
||||
|
||||
rsource "widgets/Kconfig"
|
||||
|
||||
endif
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <kernel.h>
|
||||
#include <init.h>
|
||||
#include <device.h>
|
||||
#include <devicetree.h>
|
||||
|
@ -21,6 +22,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
#define ZMK_DISPLAY_NAME CONFIG_LVGL_DISPLAY_DEV_NAME
|
||||
|
||||
static const struct device *display;
|
||||
static bool initialized = false;
|
||||
|
||||
static lv_obj_t *screen;
|
||||
|
||||
|
@ -32,19 +34,41 @@ void display_tick_cb(struct k_work *work) { lv_task_handler(); }
|
|||
|
||||
K_WORK_DEFINE(display_tick_work, display_tick_cb);
|
||||
|
||||
void display_timer_cb() {
|
||||
lv_tick_inc(TICK_MS);
|
||||
k_work_submit(&display_tick_work);
|
||||
#if IS_ENABLED(CONFIG_ZMK_DISPLAY_WORK_QUEUE_DEDICATED)
|
||||
|
||||
K_THREAD_STACK_DEFINE(display_work_stack_area, CONFIG_ZMK_DISPLAY_DEDICATED_THREAD_STACK_SIZE);
|
||||
|
||||
static struct k_work_q display_work_q;
|
||||
|
||||
#endif
|
||||
|
||||
struct k_work_q *zmk_display_work_q() {
|
||||
#if IS_ENABLED(CONFIG_ZMK_DISPLAY_WORK_QUEUE_DEDICATED)
|
||||
return &display_work_q;
|
||||
#else
|
||||
return &k_sys_work_q;
|
||||
#endif
|
||||
}
|
||||
|
||||
void display_timer_cb() {
|
||||
lv_tick_inc(TICK_MS);
|
||||
k_work_submit_to_queue(zmk_display_work_q(), &display_tick_work);
|
||||
}
|
||||
|
||||
void blank_display_cb(struct k_work *work) { display_blanking_on(display); }
|
||||
|
||||
void unblank_display_cb(struct k_work *work) { display_blanking_off(display); }
|
||||
|
||||
K_TIMER_DEFINE(display_timer, display_timer_cb, NULL);
|
||||
K_WORK_DEFINE(blank_display_work, blank_display_cb);
|
||||
K_WORK_DEFINE(unblank_display_work, unblank_display_cb);
|
||||
|
||||
static void start_display_updates() {
|
||||
if (display == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
display_blanking_off(display);
|
||||
k_work_submit_to_queue(zmk_display_work_q(), &unblank_display_work);
|
||||
|
||||
k_timer_start(&display_timer, K_MSEC(TICK_MS), K_MSEC(TICK_MS));
|
||||
}
|
||||
|
@ -54,11 +78,13 @@ static void stop_display_updates() {
|
|||
return;
|
||||
}
|
||||
|
||||
display_blanking_on(display);
|
||||
k_work_submit_to_queue(zmk_display_work_q(), &blank_display_work);
|
||||
|
||||
k_timer_stop(&display_timer);
|
||||
}
|
||||
|
||||
int zmk_display_is_initialized() { return initialized; }
|
||||
|
||||
int zmk_display_init() {
|
||||
LOG_DBG("");
|
||||
|
||||
|
@ -68,6 +94,12 @@ int zmk_display_init() {
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_DISPLAY_WORK_QUEUE_DEDICATED)
|
||||
k_work_q_start(&display_work_q, display_work_stack_area,
|
||||
K_THREAD_STACK_SIZEOF(display_work_stack_area),
|
||||
CONFIG_ZMK_DISPLAY_DEDICATED_THREAD_PRIORITY);
|
||||
#endif
|
||||
|
||||
screen = zmk_display_status_screen();
|
||||
|
||||
if (screen == NULL) {
|
||||
|
@ -79,6 +111,8 @@ int zmk_display_init() {
|
|||
|
||||
start_display_updates();
|
||||
|
||||
initialized = true;
|
||||
|
||||
LOG_DBG("");
|
||||
return 0;
|
||||
}
|
||||
|
@ -105,4 +139,4 @@ int display_event_handler(const zmk_event_t *eh) {
|
|||
}
|
||||
|
||||
ZMK_LISTENER(display, display_event_handler);
|
||||
ZMK_SUBSCRIPTION(display, zmk_activity_state_changed);
|
||||
ZMK_SUBSCRIPTION(display, zmk_activity_state_changed);
|
||||
|
|
|
@ -7,6 +7,7 @@ config ZMK_WIDGET_LAYER_STATUS
|
|||
bool "Widget for highest, active layer using small icons"
|
||||
default y
|
||||
depends on !ZMK_SPLIT || ZMK_SPLIT_BLE_ROLE_CENTRAL
|
||||
select LVGL_USE_LABEL
|
||||
select LVGL_FONT_MONTSERRAT_12
|
||||
|
||||
config ZMK_WIDGET_BATTERY_STATUS
|
||||
|
|
|
@ -4,11 +4,13 @@
|
|||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <kernel.h>
|
||||
#include <bluetooth/services/bas.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#include <zmk/display.h>
|
||||
#include <zmk/display/widgets/battery_status.h>
|
||||
#include <zmk/usb.h>
|
||||
#include <zmk/events/usb_conn_state_changed.h>
|
||||
|
@ -33,12 +35,20 @@ void battery_status_init() {
|
|||
lv_style_set_text_line_space(&label_style, LV_STATE_DEFAULT, 1);
|
||||
}
|
||||
|
||||
void set_battery_symbol(lv_obj_t *label) {
|
||||
struct battery_status_state {
|
||||
uint8_t level;
|
||||
#if IS_ENABLED(CONFIG_USB)
|
||||
bool usb_present;
|
||||
#endif
|
||||
};
|
||||
|
||||
static void set_battery_symbol(lv_obj_t *label, struct battery_status_state state) {
|
||||
char text[2] = " ";
|
||||
uint8_t level = bt_bas_get_battery_level();
|
||||
|
||||
uint8_t level = state.level;
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB)
|
||||
if (zmk_usb_is_powered()) {
|
||||
if (state.usb_present) {
|
||||
strcpy(text, LV_SYMBOL_CHARGE);
|
||||
}
|
||||
#endif /* IS_ENABLED(CONFIG_USB) */
|
||||
|
@ -57,32 +67,41 @@ void set_battery_symbol(lv_obj_t *label) {
|
|||
lv_label_set_text(label, text);
|
||||
}
|
||||
|
||||
void battery_status_update_cb(struct battery_status_state state) {
|
||||
struct zmk_widget_battery_status *widget;
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_battery_symbol(widget->obj, state); }
|
||||
}
|
||||
|
||||
static struct battery_status_state battery_status_get_state(const zmk_event_t *eh) {
|
||||
return (struct battery_status_state) {
|
||||
.level = bt_bas_get_battery_level(),
|
||||
#if IS_ENABLED(CONFIG_USB)
|
||||
.usb_present = zmk_usb_is_powered(),
|
||||
#endif /* IS_ENABLED(CONFIG_USB) */
|
||||
};
|
||||
}
|
||||
|
||||
ZMK_DISPLAY_WIDGET_LISTENER(widget_battery_status, struct battery_status_state,
|
||||
battery_status_update_cb, battery_status_get_state)
|
||||
|
||||
ZMK_SUBSCRIPTION(widget_battery_status, zmk_battery_state_changed);
|
||||
#if IS_ENABLED(CONFIG_USB)
|
||||
ZMK_SUBSCRIPTION(widget_battery_status, zmk_usb_conn_state_changed);
|
||||
#endif /* IS_ENABLED(CONFIG_USB) */
|
||||
|
||||
int zmk_widget_battery_status_init(struct zmk_widget_battery_status *widget, lv_obj_t *parent) {
|
||||
battery_status_init();
|
||||
widget->obj = lv_label_create(parent, NULL);
|
||||
lv_obj_add_style(widget->obj, LV_LABEL_PART_MAIN, &label_style);
|
||||
|
||||
lv_obj_set_size(widget->obj, 40, 15);
|
||||
set_battery_symbol(widget->obj);
|
||||
|
||||
sys_slist_append(&widgets, &widget->node);
|
||||
|
||||
widget_battery_status_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
lv_obj_t *zmk_widget_battery_status_obj(struct zmk_widget_battery_status *widget) {
|
||||
LOG_DBG("Label: %p", widget->obj);
|
||||
return widget->obj;
|
||||
}
|
||||
|
||||
int battery_status_listener(const zmk_event_t *eh) {
|
||||
struct zmk_widget_battery_status *widget;
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_battery_symbol(widget->obj); }
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
|
||||
ZMK_LISTENER(widget_battery_status, battery_status_listener)
|
||||
ZMK_SUBSCRIPTION(widget_battery_status, zmk_battery_state_changed);
|
||||
#if IS_ENABLED(CONFIG_USB)
|
||||
ZMK_SUBSCRIPTION(widget_battery_status, zmk_usb_conn_state_changed);
|
||||
#endif /* IS_ENABLED(CONFIG_USB) */
|
||||
|
|
|
@ -4,9 +4,11 @@
|
|||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <kernel.h>
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#include <zmk/display.h>
|
||||
#include <zmk/display/widgets/layer_status.h>
|
||||
#include <zmk/events/layer_state_changed.h>
|
||||
#include <zmk/event_manager.h>
|
||||
|
@ -18,7 +20,12 @@ static lv_style_t label_style;
|
|||
|
||||
static bool style_initialized = false;
|
||||
|
||||
void layer_status_init() {
|
||||
struct layer_status_state {
|
||||
uint8_t index;
|
||||
const char *label;
|
||||
};
|
||||
|
||||
static void layer_status_init() {
|
||||
if (style_initialized) {
|
||||
return;
|
||||
}
|
||||
|
@ -31,49 +38,50 @@ void layer_status_init() {
|
|||
lv_style_set_text_line_space(&label_style, LV_STATE_DEFAULT, 1);
|
||||
}
|
||||
|
||||
void set_layer_symbol(lv_obj_t *label) {
|
||||
int active_layer_index = zmk_keymap_highest_layer_active();
|
||||
|
||||
LOG_DBG("Layer changed to %i", active_layer_index);
|
||||
|
||||
const char *layer_label = zmk_keymap_layer_label(active_layer_index);
|
||||
if (layer_label == NULL) {
|
||||
static void set_layer_symbol(lv_obj_t *label, struct layer_status_state state) {
|
||||
if (state.label == NULL) {
|
||||
char text[6] = {};
|
||||
|
||||
sprintf(text, LV_SYMBOL_KEYBOARD "%i", active_layer_index);
|
||||
sprintf(text, LV_SYMBOL_KEYBOARD "%i", state.index);
|
||||
|
||||
lv_label_set_text(label, text);
|
||||
} else {
|
||||
char text[12] = {};
|
||||
|
||||
snprintf(text, 12, LV_SYMBOL_KEYBOARD "%s", layer_label);
|
||||
snprintf(text, 12, LV_SYMBOL_KEYBOARD "%s", state.label);
|
||||
|
||||
lv_label_set_text(label, text);
|
||||
}
|
||||
}
|
||||
|
||||
static void layer_status_update_cb(struct layer_status_state state) {
|
||||
struct zmk_widget_layer_status *widget;
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_layer_symbol(widget->obj, state); }
|
||||
}
|
||||
|
||||
static struct layer_status_state layer_status_get_state(const zmk_event_t *eh) {
|
||||
uint8_t index = zmk_keymap_highest_layer_active();
|
||||
return (struct layer_status_state){.index = index, .label = zmk_keymap_layer_label(index)};
|
||||
}
|
||||
|
||||
ZMK_DISPLAY_WIDGET_LISTENER(widget_layer_status, struct layer_status_state, layer_status_update_cb,
|
||||
layer_status_get_state)
|
||||
|
||||
ZMK_SUBSCRIPTION(widget_layer_status, zmk_layer_state_changed);
|
||||
|
||||
int zmk_widget_layer_status_init(struct zmk_widget_layer_status *widget, lv_obj_t *parent) {
|
||||
layer_status_init();
|
||||
widget->obj = lv_label_create(parent, NULL);
|
||||
lv_obj_add_style(widget->obj, LV_LABEL_PART_MAIN, &label_style);
|
||||
|
||||
lv_obj_set_size(widget->obj, 40, 15);
|
||||
set_layer_symbol(widget->obj);
|
||||
|
||||
sys_slist_append(&widgets, &widget->node);
|
||||
|
||||
widget_layer_status_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
lv_obj_t *zmk_widget_layer_status_obj(struct zmk_widget_layer_status *widget) {
|
||||
return widget->obj;
|
||||
}
|
||||
|
||||
int layer_status_listener(const zmk_event_t *eh) {
|
||||
struct zmk_widget_layer_status *widget;
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_layer_symbol(widget->obj); }
|
||||
return 0;
|
||||
}
|
||||
|
||||
ZMK_LISTENER(widget_layer_status, layer_status_listener)
|
||||
ZMK_SUBSCRIPTION(widget_layer_status, zmk_layer_state_changed);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue