Merge remote-tracking branch 'upstream/main' into Zodiark

This commit is contained in:
Aleblazer 2021-09-25 17:47:14 -05:00
commit 958616990f
148 changed files with 18998 additions and 7415 deletions

View file

@ -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

View 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

View file

@ -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
View file

@ -1 +1,2 @@
build/
node_modules/

3
app/.prettierrc.js Normal file
View file

@ -0,0 +1,3 @@
module.exports = {
endOfLine: "auto",
};

View file

@ -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)

View file

@ -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

View 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

View 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

View 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]

View 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

View 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

View 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
```

View 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)

View 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>;
};
};
};

View 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
&lt { 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
&lt NAV_L TAB &kp ENTER &lt NUM_L SPACE &lt 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
>;
};
};
};

View 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

View 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

View 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

View 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

View file

@ -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

View 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]

View file

@ -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>;
};
};

View 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]

View 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]

View 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]

View 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]

View 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]

View file

@ -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 {

View 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

View 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]

View file

@ -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.

View 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.

View 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

View 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)

View 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

View file

@ -0,0 +1,2 @@
# Copyright (c) 2021 The ZMK Contributors
# SPDX-License-Identifier: MIT

View 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)>
;
};
};

View 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
>;
};
};
};

View 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

View file

@ -0,0 +1,7 @@
/*
* Copyright (c) 2021 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include "a_dux.dtsi"

View file

@ -0,0 +1,11 @@
/*
* Copyright (c) 2021 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include "a_dux.dtsi"
&default_transform {
col-offset = <17>;
};

View 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

View 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

View file

@ -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
>;
};
};

View 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

View file

@ -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 (`{...}`).

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View file

@ -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)

View file

@ -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"

View file

@ -0,0 +1,4 @@
# Copyright (c) 2021 The ZMK Contributors
# SPDX-License-Identifier: MIT
zephyr_sources_ifdef(CONFIG_IL0323 il0323.c)

View file

@ -0,0 +1,4 @@
# Copyright (c) 2021 The ZMK Contributors
# SPDX-License-Identifier: MIT
rsource "Kconfig.il0323"

View 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.

View 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);

View 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_ */

View 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
View file

@ -0,0 +1 @@
rsource "Kconfig.mcp23017"

View 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

View 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)

View 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_ */

View file

@ -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;
}

View file

@ -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)

View 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

View file

@ -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

View 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

View file

@ -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);

View file

@ -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();

View 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,
};

View 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
View 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
View 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"
}
}

View 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

View file

@ -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

View 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)

View file

@ -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) */

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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) */

View file

@ -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