Merge remote-tracking branch 'qmk/master' into merge-2023-06-03
This commit is contained in:
commit
d6e95213e3
4
.clangd
Normal file
4
.clangd
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
CompileFlags:
|
||||||
|
Add: [-Wno-unknown-attributes, -Wno-maybe-uninitialized, -Wno-unknown-warning-option]
|
||||||
|
Remove: [-W*, -mcall-prologues]
|
||||||
|
Compiler: clang
|
54
.github/workflows/ci_builds.yml
vendored
54
.github/workflows/ci_builds.yml
vendored
@ -5,25 +5,29 @@ permissions:
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches: [master, develop]
|
||||||
- master
|
workflow_dispatch:
|
||||||
- develop
|
inputs:
|
||||||
|
branch:
|
||||||
|
type: choice
|
||||||
|
description: 'Branch to build'
|
||||||
|
options: [master, develop]
|
||||||
|
|
||||||
|
concurrency: ci_build-${{ github.event.inputs.branch || github.ref_name }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ci_builds:
|
ci_builds:
|
||||||
|
if: github.repository == 'qmk/qmk_firmware'
|
||||||
name: "CI Build"
|
name: "CI Build"
|
||||||
runs-on: self-hosted
|
runs-on: self-hosted
|
||||||
timeout-minutes: 1380
|
timeout-minutes: 1380
|
||||||
|
|
||||||
if: github.repository == 'qmk/qmk_firmware'
|
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
keymap:
|
keymap: [default, via]
|
||||||
- default
|
|
||||||
- via
|
|
||||||
|
|
||||||
container: qmkfm/qmk_cli
|
container: ghcr.io/qmk/qmk_cli
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Disable safe.directory check
|
- name: Disable safe.directory check
|
||||||
@ -32,9 +36,39 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
ref: ${{ github.event.inputs.branch || github.ref }}
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pip3 install -r requirements.txt
|
run: pip3 install -r requirements.txt
|
||||||
|
|
||||||
- name: Run `qmk mass-compile` (keymap ${{ matrix.keymap }})
|
- name: Run `qmk mass-compile` (keymap ${{ matrix.keymap }})
|
||||||
run: qmk mass-compile -j $(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null) -km ${{ matrix.keymap }}
|
run: |
|
||||||
|
export NCPUS=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null)
|
||||||
|
qmk mass-compile -t -j $NCPUS -km ${{ matrix.keymap }} -e DUMP_CI_METADATA=yes || touch .failed
|
||||||
|
# Generate the step summary markdown
|
||||||
|
./util/ci/generate_failure_markdown.sh > $GITHUB_STEP_SUMMARY || true
|
||||||
|
# Truncate to a maximum of 1MB to deal with GitHub workflow limit
|
||||||
|
truncate --size='<960K' $GITHUB_STEP_SUMMARY || true
|
||||||
|
# Exit with failure if the compilation stage failed
|
||||||
|
[ ! -f .failed ] || exit 1
|
||||||
|
|
||||||
|
- name: 'Upload artifacts'
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: artifacts-${{ github.event.inputs.branch || github.ref_name }}-${{ matrix.keymap }}
|
||||||
|
if-no-files-found: ignore
|
||||||
|
path: |
|
||||||
|
*.bin
|
||||||
|
*.hex
|
||||||
|
*.uf2
|
||||||
|
.build/failed.*
|
||||||
|
|
||||||
|
- name: 'CI Discord Notification'
|
||||||
|
if: always()
|
||||||
|
working-directory: util/ci/
|
||||||
|
env:
|
||||||
|
DISCORD_WEBHOOK: ${{ secrets.CI_DISCORD_WEBHOOK }}
|
||||||
|
run: |
|
||||||
|
python3 -m pip install -r requirements.txt
|
||||||
|
python3 ./discord-results.py --branch ${{ github.event.inputs.branch || github.ref_name }} --keymap ${{ matrix.keymap }} --url ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
2
.github/workflows/regen.yml
vendored
2
.github/workflows/regen.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
regen:
|
regen:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
container: qmkfm/qmk_cli
|
container: ghcr.io/qmk/qmk_cli
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Disable safe.directory check
|
- name: Disable safe.directory check
|
||||||
|
4
.github/workflows/regen_push.yml
vendored
4
.github/workflows/regen_push.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
regen:
|
regen:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
container: qmkfm/qmk_cli
|
container: ghcr.io/qmk/qmk_cli
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Disable safe.directory check
|
- name: Disable safe.directory check
|
||||||
@ -34,7 +34,7 @@ jobs:
|
|||||||
git config user.email 'hello@qmk.fm'
|
git config user.email 'hello@qmk.fm'
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v4
|
uses: peter-evans/create-pull-request@v5
|
||||||
if: ${{ github.repository == 'qmk/qmk_firmware'}}
|
if: ${{ github.repository == 'qmk/qmk_firmware'}}
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.QMK_BOT_TOKEN }}
|
token: ${{ secrets.QMK_BOT_TOKEN }}
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -31,6 +31,9 @@ quantum/version.h
|
|||||||
*.uf2
|
*.uf2
|
||||||
*.vfw
|
*.vfw
|
||||||
|
|
||||||
|
# DD config at wrong location
|
||||||
|
/keyboards/**/keymaps/*/info.json
|
||||||
|
|
||||||
# Old-style QMK Makefiles
|
# Old-style QMK Makefiles
|
||||||
/keyboards/**/Makefile
|
/keyboards/**/Makefile
|
||||||
|
|
||||||
@ -46,7 +49,6 @@ quantum/version.h
|
|||||||
.idea/
|
.idea/
|
||||||
.project
|
.project
|
||||||
.settings/
|
.settings/
|
||||||
.vagrant/
|
|
||||||
|
|
||||||
# ?
|
# ?
|
||||||
.dep
|
.dep
|
||||||
|
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@ -3,7 +3,7 @@
|
|||||||
"recommendations": [
|
"recommendations": [
|
||||||
"EditorConfig.EditorConfig",
|
"EditorConfig.EditorConfig",
|
||||||
"xaver.clang-format",
|
"xaver.clang-format",
|
||||||
"ms-vscode.cpptools",
|
"llvm-vs-code-extensions.vscode-clangd",
|
||||||
"bierner.github-markdown-preview",
|
"bierner.github-markdown-preview",
|
||||||
"donjayamanne.git-extension-pack"
|
"donjayamanne.git-extension-pack"
|
||||||
]
|
]
|
||||||
|
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
@ -26,6 +26,9 @@
|
|||||||
},
|
},
|
||||||
"python.formatting.provider": "yapf",
|
"python.formatting.provider": "yapf",
|
||||||
"[json]": {
|
"[json]": {
|
||||||
"editor.formatOnSave": false
|
"editor.formatOnSave": false
|
||||||
}
|
},
|
||||||
|
"clangd.arguments": [
|
||||||
|
"--header-insertion=never"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
FROM qmkfm/qmk_cli
|
|
||||||
|
|
||||||
VOLUME /qmk_firmware
|
|
||||||
WORKDIR /qmk_firmware
|
|
||||||
|
|
||||||
CMD qmk compile -kb all -km default
|
|
95
Vagrantfile
vendored
95
Vagrantfile
vendored
@ -1,95 +0,0 @@
|
|||||||
# -*- mode: ruby -*-
|
|
||||||
# vi: set ft=ruby :
|
|
||||||
|
|
||||||
Vagrant.configure(2) do |config|
|
|
||||||
# define a name instead of just 'default'
|
|
||||||
config.vm.define "qmk_firmware"
|
|
||||||
|
|
||||||
# VMware/Virtualbox ( and also Hyperv/Parallels) 64 bit
|
|
||||||
config.vm.box = "generic/debian10"
|
|
||||||
|
|
||||||
config.vm.synced_folder '.', '/vagrant'
|
|
||||||
|
|
||||||
# This section allows you to customize the Virtualbox VM
|
|
||||||
# settings, ie showing the GUI or upping the memory
|
|
||||||
# or cores if desired
|
|
||||||
config.vm.provider "virtualbox" do |vb|
|
|
||||||
# Hide the VirtualBox GUI when booting the machine
|
|
||||||
vb.gui = false
|
|
||||||
# Uncomment the below lines if you want to program
|
|
||||||
# your Teensy via the VM rather than your host OS
|
|
||||||
#vb.customize ['modifyvm', :id, '--usb', 'on']
|
|
||||||
#vb.customize ['usbfilter', 'add', '0',
|
|
||||||
# '--target', :id,
|
|
||||||
# '--name', 'teensy',
|
|
||||||
# '--vendorid', '0x16c0',
|
|
||||||
# '--productid','0x0478'
|
|
||||||
# ]
|
|
||||||
# Customize the amount of memory on the VM:
|
|
||||||
vb.memory = "512"
|
|
||||||
# Uncomment the below lines if you have time sync
|
|
||||||
# issues with make and incremental builds
|
|
||||||
#vb.customize [ "guestproperty", "set", :id, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-threshold", 1000 ]
|
|
||||||
end
|
|
||||||
|
|
||||||
# This section allows you to customize the VMware VM
|
|
||||||
# settings, ie showing the GUI or upping the memory
|
|
||||||
# or cores if desired
|
|
||||||
config.vm.provider "vmware_workstation" do |vmw|
|
|
||||||
# Hide the VMware GUI when booting the machine
|
|
||||||
vmw.gui = false
|
|
||||||
|
|
||||||
# Customize the amount of memory on the VM:
|
|
||||||
vmw.memory = "512"
|
|
||||||
end
|
|
||||||
|
|
||||||
config.vm.provider "vmware_fusion" do |vmf|
|
|
||||||
# Hide the vmfare GUI when booting the machine
|
|
||||||
vmf.gui = false
|
|
||||||
|
|
||||||
# Customize the amount of memory on the VM:
|
|
||||||
vmf.memory = "512"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Docker provider pulls from hub.docker.com respecting docker.image if
|
|
||||||
# config.vm.box is nil. In this case, we adhoc build util/vagrant/Dockerfile.
|
|
||||||
# Note that this bind-mounts from the current dir to
|
|
||||||
# /vagrant in the guest, so unless your UID is 1000 to match vagrant in the
|
|
||||||
# image, you'll need to: chmod -R a+rw .
|
|
||||||
config.vm.provider "docker" do |docker, override|
|
|
||||||
override.vm.box = nil
|
|
||||||
docker.build_dir = "util/vagrant"
|
|
||||||
docker.has_ssh = true
|
|
||||||
end
|
|
||||||
|
|
||||||
# Unless we are running the docker container directly
|
|
||||||
# 1. run container detached on vm
|
|
||||||
# 2. attach on 'vagrant ssh'
|
|
||||||
["virtualbox", "vmware_workstation", "vmware_fusion"].each do |type|
|
|
||||||
config.vm.provider type do |virt, override|
|
|
||||||
override.vm.provision "docker" do |d|
|
|
||||||
d.run "qmkfm/qmk_cli",
|
|
||||||
cmd: "tail -f /dev/null",
|
|
||||||
args: "--privileged -v /dev:/dev -v '/vagrant:/vagrant'"
|
|
||||||
end
|
|
||||||
|
|
||||||
override.vm.provision "shell", inline: <<-SHELL
|
|
||||||
echo 'docker restart qmkfm-qmk_cli && exec docker exec -it qmkfm-qmk_cli /bin/bash -l' >> ~vagrant/.bashrc
|
|
||||||
SHELL
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
config.vm.post_up_message = <<-EOT
|
|
||||||
|
|
||||||
Log into the environment using 'vagrant ssh'. QMK directory synchronized with
|
|
||||||
host is located at /vagrant
|
|
||||||
To compile the .hex files use make command inside this directory, e.g.
|
|
||||||
cd /vagrant
|
|
||||||
make <keyboard>:default
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
make planck/rev4:default:dfu
|
|
||||||
make planck/rev4:default
|
|
||||||
|
|
||||||
EOT
|
|
||||||
end
|
|
@ -29,6 +29,11 @@ KEYBOARD_FILESAFE := $(subst /,_,$(KEYBOARD))
|
|||||||
TARGET ?= $(KEYBOARD_FILESAFE)_$(KEYMAP)
|
TARGET ?= $(KEYBOARD_FILESAFE)_$(KEYMAP)
|
||||||
KEYBOARD_OUTPUT := $(BUILD_DIR)/obj_$(KEYBOARD_FILESAFE)
|
KEYBOARD_OUTPUT := $(BUILD_DIR)/obj_$(KEYBOARD_FILESAFE)
|
||||||
|
|
||||||
|
ifeq ($(strip $(DUMP_CI_METADATA)),yes)
|
||||||
|
$(info CI Metadata: KEYBOARD=$(KEYBOARD))
|
||||||
|
$(info CI Metadata: KEYMAP=$(KEYMAP))
|
||||||
|
endif
|
||||||
|
|
||||||
# Force expansion
|
# Force expansion
|
||||||
TARGET := $(TARGET)
|
TARGET := $(TARGET)
|
||||||
|
|
||||||
@ -339,6 +344,15 @@ $(KEYBOARD_OUTPUT)/src/default_keyboard.h: $(INFO_JSON_FILES)
|
|||||||
|
|
||||||
generated-files: $(KEYBOARD_OUTPUT)/src/info_config.h $(KEYBOARD_OUTPUT)/src/default_keyboard.c $(KEYBOARD_OUTPUT)/src/default_keyboard.h
|
generated-files: $(KEYBOARD_OUTPUT)/src/info_config.h $(KEYBOARD_OUTPUT)/src/default_keyboard.c $(KEYBOARD_OUTPUT)/src/default_keyboard.h
|
||||||
|
|
||||||
|
generated-files: $(KEYMAP_OUTPUT)/src/info_deps.d
|
||||||
|
|
||||||
|
$(KEYMAP_OUTPUT)/src/info_deps.d:
|
||||||
|
@$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD)
|
||||||
|
$(eval CMD=$(QMK_BIN) generate-make-dependencies -kb $(KEYBOARD) -km $(KEYMAP) -o $(KEYMAP_OUTPUT)/src/info_deps.d)
|
||||||
|
@$(BUILD_CMD)
|
||||||
|
|
||||||
|
-include $(KEYMAP_OUTPUT)/src/info_deps.d
|
||||||
|
|
||||||
.INTERMEDIATE : generated-files
|
.INTERMEDIATE : generated-files
|
||||||
|
|
||||||
# Userspace setup and definitions
|
# Userspace setup and definitions
|
||||||
@ -442,6 +456,14 @@ $(eval $(call add_qmk_prefix_defs,MCU_FAMILY,MCU_FAMILY))
|
|||||||
$(eval $(call add_qmk_prefix_defs,MCU_SERIES,MCU_SERIES))
|
$(eval $(call add_qmk_prefix_defs,MCU_SERIES,MCU_SERIES))
|
||||||
$(eval $(call add_qmk_prefix_defs,BOARD,BOARD))
|
$(eval $(call add_qmk_prefix_defs,BOARD,BOARD))
|
||||||
|
|
||||||
|
# Control whether intermediate file listings are generated
|
||||||
|
# e.g.:
|
||||||
|
# make handwired/onekey/blackpill_f411:default KEEP_INTERMEDIATES=yes
|
||||||
|
# cat .build/obj_handwired_onekey_blackpill_f411_default/quantum/quantum.i | sed -e 's@^#.*@@g' -e 's@^\s*//.*@@g' -e '/^\s*$/d' | clang-format
|
||||||
|
ifeq ($(strip $(KEEP_INTERMEDIATES)), yes)
|
||||||
|
OPT_DEFS += -save-temps=obj
|
||||||
|
endif
|
||||||
|
|
||||||
# TODO: remove this bodge?
|
# TODO: remove this bodge?
|
||||||
PROJECT_DEFS := $(OPT_DEFS)
|
PROJECT_DEFS := $(OPT_DEFS)
|
||||||
PROJECT_INC := $(VPATH) $(EXTRAINCDIRS) $(KEYBOARD_PATHS)
|
PROJECT_INC := $(VPATH) $(EXTRAINCDIRS) $(KEYBOARD_PATHS)
|
||||||
|
@ -75,6 +75,10 @@ $(TEST)_SRC += \
|
|||||||
tests/test_common/main.cpp \
|
tests/test_common/main.cpp \
|
||||||
$(QUANTUM_PATH)/logging/print.c
|
$(QUANTUM_PATH)/logging/print.c
|
||||||
|
|
||||||
|
ifneq ($(strip $(INTROSPECTION_KEYMAP_C)),)
|
||||||
|
$(TEST)_DEFS += -DINTROSPECTION_KEYMAP_C=\"$(strip $(INTROSPECTION_KEYMAP_C))\"
|
||||||
|
endif
|
||||||
|
|
||||||
$(TEST_OBJ)/$(TEST)_SRC := $($(TEST)_SRC)
|
$(TEST_OBJ)/$(TEST)_SRC := $($(TEST)_SRC)
|
||||||
$(TEST_OBJ)/$(TEST)_INC := $($(TEST)_INC) $(VPATH) $(GTEST_INC)
|
$(TEST_OBJ)/$(TEST)_INC := $($(TEST)_INC) $(VPATH) $(GTEST_INC)
|
||||||
$(TEST_OBJ)/$(TEST)_DEFS := $($(TEST)_DEFS)
|
$(TEST_OBJ)/$(TEST)_DEFS := $($(TEST)_DEFS)
|
||||||
|
@ -134,7 +134,7 @@ ifeq ($(strip $(MOUSEKEY_ENABLE)), yes)
|
|||||||
SRC += $(QUANTUM_DIR)/mousekey.c
|
SRC += $(QUANTUM_DIR)/mousekey.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
VALID_POINTING_DEVICE_DRIVER_TYPES := adns5050 adns9800 analog_joystick cirque_pinnacle_i2c cirque_pinnacle_spi paw3204 pmw3360 pmw3389 pimoroni_trackball custom
|
VALID_POINTING_DEVICE_DRIVER_TYPES := adns5050 adns9800 analog_joystick cirque_pinnacle_i2c cirque_pinnacle_spi paw3204 pmw3320 pmw3360 pmw3389 pimoroni_trackball custom
|
||||||
ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes)
|
ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes)
|
||||||
ifeq ($(filter $(POINTING_DEVICE_DRIVER),$(VALID_POINTING_DEVICE_DRIVER_TYPES)),)
|
ifeq ($(filter $(POINTING_DEVICE_DRIVER),$(VALID_POINTING_DEVICE_DRIVER_TYPES)),)
|
||||||
$(call CATASTROPHIC_ERROR,Invalid POINTING_DEVICE_DRIVER,POINTING_DEVICE_DRIVER="$(POINTING_DEVICE_DRIVER)" is not a valid pointing device type)
|
$(call CATASTROPHIC_ERROR,Invalid POINTING_DEVICE_DRIVER,POINTING_DEVICE_DRIVER="$(POINTING_DEVICE_DRIVER)" is not a valid pointing device type)
|
||||||
@ -213,10 +213,10 @@ else
|
|||||||
SRC += eeprom_driver.c eeprom_spi.c
|
SRC += eeprom_driver.c eeprom_spi.c
|
||||||
else ifeq ($(strip $(EEPROM_DRIVER)), legacy_stm32_flash)
|
else ifeq ($(strip $(EEPROM_DRIVER)), legacy_stm32_flash)
|
||||||
# STM32 Emulated EEPROM, backed by MCU flash (soon to be deprecated)
|
# STM32 Emulated EEPROM, backed by MCU flash (soon to be deprecated)
|
||||||
OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_STM32_FLASH_EMULATED
|
OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_LEGACY_EMULATED_FLASH
|
||||||
COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/flash
|
COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/flash
|
||||||
COMMON_VPATH += $(DRIVER_PATH)/flash
|
COMMON_VPATH += $(DRIVER_PATH)/flash
|
||||||
SRC += eeprom_driver.c eeprom_stm32.c flash_stm32.c
|
SRC += eeprom_driver.c eeprom_legacy_emulated_flash.c legacy_flash_ops.c
|
||||||
else ifeq ($(strip $(EEPROM_DRIVER)), transient)
|
else ifeq ($(strip $(EEPROM_DRIVER)), transient)
|
||||||
# Transient EEPROM implementation -- no data storage but provides runtime area for it
|
# Transient EEPROM implementation -- no data storage but provides runtime area for it
|
||||||
OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_TRANSIENT
|
OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_TRANSIENT
|
||||||
@ -419,7 +419,7 @@ endif
|
|||||||
|
|
||||||
RGB_MATRIX_ENABLE ?= no
|
RGB_MATRIX_ENABLE ?= no
|
||||||
|
|
||||||
VALID_RGB_MATRIX_TYPES := AW20216 IS31FL3731 IS31FL3733 IS31FL3737 IS31FL3741 IS31FL3742A IS31FL3743A IS31FL3745 IS31FL3746A CKLED2001 WS2812 custom
|
VALID_RGB_MATRIX_TYPES := AW20216 IS31FL3731 IS31FL3733 IS31FL3736 IS31FL3737 IS31FL3741 IS31FL3742A IS31FL3743A IS31FL3745 IS31FL3746A CKLED2001 WS2812 custom
|
||||||
ifeq ($(strip $(RGB_MATRIX_ENABLE)), yes)
|
ifeq ($(strip $(RGB_MATRIX_ENABLE)), yes)
|
||||||
ifeq ($(filter $(RGB_MATRIX_DRIVER),$(VALID_RGB_MATRIX_TYPES)),)
|
ifeq ($(filter $(RGB_MATRIX_DRIVER),$(VALID_RGB_MATRIX_TYPES)),)
|
||||||
$(call CATASTROPHIC_ERROR,Invalid RGB_MATRIX_DRIVER,RGB_MATRIX_DRIVER="$(RGB_MATRIX_DRIVER)" is not a valid matrix type)
|
$(call CATASTROPHIC_ERROR,Invalid RGB_MATRIX_DRIVER,RGB_MATRIX_DRIVER="$(RGB_MATRIX_DRIVER)" is not a valid matrix type)
|
||||||
@ -460,6 +460,13 @@ endif
|
|||||||
QUANTUM_LIB_SRC += i2c_master.c
|
QUANTUM_LIB_SRC += i2c_master.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(RGB_MATRIX_DRIVER)), IS31FL3736)
|
||||||
|
OPT_DEFS += -DIS31FL3736 -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||||
|
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||||
|
SRC += is31fl3736.c
|
||||||
|
QUANTUM_LIB_SRC += i2c_master.c
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), IS31FL3737)
|
ifeq ($(strip $(RGB_MATRIX_DRIVER)), IS31FL3737)
|
||||||
OPT_DEFS += -DIS31FL3737 -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
OPT_DEFS += -DIS31FL3737 -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||||
@ -574,7 +581,7 @@ ifeq ($(strip $(BACKLIGHT_ENABLE)), yes)
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
VALID_WS2812_DRIVER_TYPES := bitbang pwm spi i2c vendor
|
VALID_WS2812_DRIVER_TYPES := bitbang custom i2c pwm spi vendor
|
||||||
|
|
||||||
WS2812_DRIVER ?= bitbang
|
WS2812_DRIVER ?= bitbang
|
||||||
ifeq ($(strip $(WS2812_DRIVER_REQUIRED)), yes)
|
ifeq ($(strip $(WS2812_DRIVER_REQUIRED)), yes)
|
||||||
@ -584,15 +591,11 @@ ifeq ($(strip $(WS2812_DRIVER_REQUIRED)), yes)
|
|||||||
|
|
||||||
OPT_DEFS += -DWS2812_DRIVER_$(strip $(shell echo $(WS2812_DRIVER) | tr '[:lower:]' '[:upper:]'))
|
OPT_DEFS += -DWS2812_DRIVER_$(strip $(shell echo $(WS2812_DRIVER) | tr '[:lower:]' '[:upper:]'))
|
||||||
|
|
||||||
ifeq ($(strip $(WS2812_DRIVER)), bitbang)
|
SRC += ws2812_$(strip $(WS2812_DRIVER)).c
|
||||||
SRC += ws2812.c
|
|
||||||
else
|
|
||||||
SRC += ws2812_$(strip $(WS2812_DRIVER)).c
|
|
||||||
|
|
||||||
ifeq ($(strip $(PLATFORM)), CHIBIOS)
|
ifeq ($(strip $(PLATFORM)), CHIBIOS)
|
||||||
ifeq ($(strip $(WS2812_DRIVER)), pwm)
|
ifeq ($(strip $(WS2812_DRIVER)), pwm)
|
||||||
OPT_DEFS += -DSTM32_DMA_REQUIRED=TRUE
|
OPT_DEFS += -DSTM32_DMA_REQUIRED=TRUE
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@ -787,17 +790,28 @@ endif
|
|||||||
|
|
||||||
VALID_OLED_DRIVER_TYPES := SSD1306 custom
|
VALID_OLED_DRIVER_TYPES := SSD1306 custom
|
||||||
OLED_DRIVER ?= SSD1306
|
OLED_DRIVER ?= SSD1306
|
||||||
|
VALID_OLED_TRANSPORT_TYPES := i2c spi custom
|
||||||
|
OLED_TRANSPORT ?= i2c
|
||||||
ifeq ($(strip $(OLED_ENABLE)), yes)
|
ifeq ($(strip $(OLED_ENABLE)), yes)
|
||||||
ifeq ($(filter $(OLED_DRIVER),$(VALID_OLED_DRIVER_TYPES)),)
|
ifeq ($(filter $(OLED_DRIVER),$(VALID_OLED_DRIVER_TYPES)),)
|
||||||
$(call CATASTROPHIC_ERROR,Invalid OLED_DRIVER,OLED_DRIVER="$(OLED_DRIVER)" is not a valid OLED driver)
|
$(call CATASTROPHIC_ERROR,Invalid OLED_DRIVER,OLED_DRIVER="$(OLED_DRIVER)" is not a valid OLED driver)
|
||||||
else
|
else
|
||||||
OPT_DEFS += -DOLED_ENABLE
|
ifeq ($(filter $(OLED_TRANSPORT),$(VALID_OLED_TRANSPORT_TYPES)),)
|
||||||
COMMON_VPATH += $(DRIVER_PATH)/oled
|
$(call CATASTROPHIC_ERROR,Invalid OLED_TRANSPORT,OLED_TRANSPORT="$(OLED_TRANSPORT)" is not a valid OLED transport)
|
||||||
|
else
|
||||||
|
OPT_DEFS += -DOLED_ENABLE
|
||||||
|
COMMON_VPATH += $(DRIVER_PATH)/oled
|
||||||
|
ifneq ($(strip $(OLED_DRIVER)), custom)
|
||||||
|
SRC += oled_driver.c
|
||||||
|
endif
|
||||||
|
|
||||||
OPT_DEFS += -DOLED_DRIVER_$(strip $(shell echo $(OLED_DRIVER) | tr '[:lower:]' '[:upper:]'))
|
OPT_DEFS += -DOLED_TRANSPORT_$(strip $(shell echo $(OLED_TRANSPORT) | tr '[:lower:]' '[:upper:]'))
|
||||||
ifeq ($(strip $(OLED_DRIVER)), SSD1306)
|
ifeq ($(strip $(OLED_TRANSPORT)), i2c)
|
||||||
SRC += ssd1306_sh1106.c
|
QUANTUM_LIB_SRC += i2c_master.c
|
||||||
QUANTUM_LIB_SRC += i2c_master.c
|
endif
|
||||||
|
ifeq ($(strip $(OLED_TRANSPORT)), spi)
|
||||||
|
QUANTUM_LIB_SRC += spi_master.c
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
@ -859,9 +873,9 @@ endif
|
|||||||
|
|
||||||
ifeq ($(strip $(PS2_MOUSE_ENABLE)), yes)
|
ifeq ($(strip $(PS2_MOUSE_ENABLE)), yes)
|
||||||
PS2_ENABLE := yes
|
PS2_ENABLE := yes
|
||||||
|
MOUSE_ENABLE := yes
|
||||||
SRC += ps2_mouse.c
|
SRC += ps2_mouse.c
|
||||||
OPT_DEFS += -DPS2_MOUSE_ENABLE
|
OPT_DEFS += -DPS2_MOUSE_ENABLE
|
||||||
OPT_DEFS += -DMOUSE_ENABLE
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
VALID_PS2_DRIVER_TYPES := busywait interrupt usart vendor
|
VALID_PS2_DRIVER_TYPES := busywait interrupt usart vendor
|
||||||
@ -938,10 +952,11 @@ ifeq ($(strip $(BLUETOOTH_ENABLE)), yes)
|
|||||||
OPT_DEFS += -DBLUETOOTH_ENABLE
|
OPT_DEFS += -DBLUETOOTH_ENABLE
|
||||||
NO_USB_STARTUP_CHECK := yes
|
NO_USB_STARTUP_CHECK := yes
|
||||||
COMMON_VPATH += $(DRIVER_PATH)/bluetooth
|
COMMON_VPATH += $(DRIVER_PATH)/bluetooth
|
||||||
SRC += outputselect.c bluetooth.c
|
SRC += outputselect.c
|
||||||
|
|
||||||
ifeq ($(strip $(BLUETOOTH_DRIVER)), BluefruitLE)
|
ifeq ($(strip $(BLUETOOTH_DRIVER)), BluefruitLE)
|
||||||
OPT_DEFS += -DBLUETOOTH_BLUEFRUIT_LE -DHAL_USE_SPI=TRUE
|
OPT_DEFS += -DBLUETOOTH_BLUEFRUIT_LE -DHAL_USE_SPI=TRUE
|
||||||
|
SRC += $(DRIVER_PATH)/bluetooth/bluetooth.c
|
||||||
SRC += $(DRIVER_PATH)/bluetooth/bluefruit_le.cpp
|
SRC += $(DRIVER_PATH)/bluetooth/bluefruit_le.cpp
|
||||||
QUANTUM_LIB_SRC += analog.c
|
QUANTUM_LIB_SRC += analog.c
|
||||||
QUANTUM_LIB_SRC += spi_master.c
|
QUANTUM_LIB_SRC += spi_master.c
|
||||||
@ -949,6 +964,7 @@ ifeq ($(strip $(BLUETOOTH_ENABLE)), yes)
|
|||||||
|
|
||||||
ifeq ($(strip $(BLUETOOTH_DRIVER)), RN42)
|
ifeq ($(strip $(BLUETOOTH_DRIVER)), RN42)
|
||||||
OPT_DEFS += -DBLUETOOTH_RN42 -DHAL_USE_SERIAL=TRUE
|
OPT_DEFS += -DBLUETOOTH_RN42 -DHAL_USE_SERIAL=TRUE
|
||||||
|
SRC += $(DRIVER_PATH)/bluetooth/bluetooth.c
|
||||||
SRC += $(DRIVER_PATH)/bluetooth/rn42.c
|
SRC += $(DRIVER_PATH)/bluetooth/rn42.c
|
||||||
QUANTUM_LIB_SRC += uart.c
|
QUANTUM_LIB_SRC += uart.c
|
||||||
endif
|
endif
|
||||||
|
@ -152,6 +152,7 @@ endif
|
|||||||
# To produce a UF2 file in your build, add to your keyboard's rules.mk:
|
# To produce a UF2 file in your build, add to your keyboard's rules.mk:
|
||||||
# FIRMWARE_FORMAT = uf2
|
# FIRMWARE_FORMAT = uf2
|
||||||
UF2CONV = $(TOP_DIR)/util/uf2conv.py
|
UF2CONV = $(TOP_DIR)/util/uf2conv.py
|
||||||
|
UF2CONV_ARGS ?=
|
||||||
UF2_FAMILY ?= 0x0
|
UF2_FAMILY ?= 0x0
|
||||||
|
|
||||||
# Compiler flags to generate dependency files.
|
# Compiler flags to generate dependency files.
|
||||||
@ -219,7 +220,7 @@ gccversion :
|
|||||||
@$(BUILD_CMD)
|
@$(BUILD_CMD)
|
||||||
|
|
||||||
%.uf2: %.elf
|
%.uf2: %.elf
|
||||||
$(eval CMD=$(HEX) $< $(BUILD_DIR)/$(TARGET).tmp && $(UF2CONV) $(BUILD_DIR)/$(TARGET).tmp --output $@ --convert --family $(UF2_FAMILY) >/dev/null 2>&1)
|
$(eval CMD=$(HEX) $< $(BUILD_DIR)/$(TARGET).tmp && $(UF2CONV) $(UF2CONV_ARGS) $(BUILD_DIR)/$(TARGET).tmp --output $@ --convert --family $(UF2_FAMILY) >/dev/null 2>&1)
|
||||||
#@$(SILENT) || printf "$(MSG_EXECUTING) '$(CMD)':\n"
|
#@$(SILENT) || printf "$(MSG_EXECUTING) '$(CMD)':\n"
|
||||||
@$(SILENT) || printf "$(MSG_UF2) $@" | $(AWK_CMD)
|
@$(SILENT) || printf "$(MSG_UF2) $@" | $(AWK_CMD)
|
||||||
@$(BUILD_CMD)
|
@$(BUILD_CMD)
|
||||||
|
@ -7,7 +7,12 @@ endif
|
|||||||
|
|
||||||
# TODO: opt in rather than assume everything uses a pro micro
|
# TODO: opt in rather than assume everything uses a pro micro
|
||||||
PIN_COMPATIBLE ?= promicro
|
PIN_COMPATIBLE ?= promicro
|
||||||
|
|
||||||
|
# Remove whitespace from any rule.mk provided vars
|
||||||
|
# - env cannot be overwritten but cannot have whitespace anyway
|
||||||
|
CONVERT_TO:=$(strip $(CONVERT_TO))
|
||||||
ifneq ($(CONVERT_TO),)
|
ifneq ($(CONVERT_TO),)
|
||||||
|
|
||||||
# stash so we can overwrite env provided vars if needed
|
# stash so we can overwrite env provided vars if needed
|
||||||
ACTIVE_CONVERTER=$(CONVERT_TO)
|
ACTIVE_CONVERTER=$(CONVERT_TO)
|
||||||
|
|
||||||
@ -23,13 +28,13 @@ ifneq ($(CONVERT_TO),)
|
|||||||
TARGET := $(TARGET)_$(CONVERT_TO)
|
TARGET := $(TARGET)_$(CONVERT_TO)
|
||||||
|
|
||||||
# Configure any defaults
|
# Configure any defaults
|
||||||
OPT_DEFS += -DCONVERT_TO_$(strip $(shell echo $(CONVERT_TO) | tr '[:lower:]' '[:upper:]'))
|
OPT_DEFS += -DCONVERT_TO_$(shell echo $(CONVERT_TO) | tr '[:lower:]' '[:upper:]')
|
||||||
OPT_DEFS += -DCONVERTER_TARGET=\"$(strip $(CONVERT_TO))\"
|
OPT_DEFS += -DCONVERTER_TARGET=\"$(CONVERT_TO)\"
|
||||||
OPT_DEFS += -DCONVERTER_ENABLED
|
OPT_DEFS += -DCONVERTER_ENABLED
|
||||||
VPATH += $(CONVERTER)
|
VPATH += $(CONVERTER)
|
||||||
|
|
||||||
# Configure for "alias" - worst case it produces an idential define
|
# Configure for "alias" - worst case it produces an idential define
|
||||||
OPT_DEFS += -DCONVERT_TO_$(strip $(shell echo $(ACTIVE_CONVERTER) | tr '[:lower:]' '[:upper:]'))
|
OPT_DEFS += -DCONVERT_TO_$(shell echo $(ACTIVE_CONVERTER) | tr '[:lower:]' '[:upper:]')
|
||||||
|
|
||||||
# Finally run any converter specific logic
|
# Finally run any converter specific logic
|
||||||
include $(CONVERTER)/converter.mk
|
include $(CONVERTER)/converter.mk
|
||||||
|
@ -32,6 +32,7 @@ GENERIC_FEATURES = \
|
|||||||
KEY_OVERRIDE \
|
KEY_OVERRIDE \
|
||||||
LEADER \
|
LEADER \
|
||||||
PROGRAMMABLE_BUTTON \
|
PROGRAMMABLE_BUTTON \
|
||||||
|
REPEAT_KEY \
|
||||||
SECURE \
|
SECURE \
|
||||||
SPACE_CADET \
|
SPACE_CADET \
|
||||||
SWAP_HANDS \
|
SWAP_HANDS \
|
||||||
|
@ -85,7 +85,8 @@ OTHER_OPTION_NAMES = \
|
|||||||
SECURE_ENABLE \
|
SECURE_ENABLE \
|
||||||
CAPS_WORD_ENABLE \
|
CAPS_WORD_ENABLE \
|
||||||
AUTOCORRECT_ENABLE \
|
AUTOCORRECT_ENABLE \
|
||||||
TRI_LAYER_ENABLE
|
TRI_LAYER_ENABLE \
|
||||||
|
REPEAT_KEY_ENABLE
|
||||||
|
|
||||||
define NAME_ECHO
|
define NAME_ECHO
|
||||||
@printf " %-30s = %-16s # %s\\n" "$1" "$($1)" "$(origin $1)"
|
@printf " %-30s = %-16s # %s\\n" "$1" "$($1)" "$(origin $1)"
|
||||||
|
@ -253,7 +253,7 @@
|
|||||||
"0x002F": {
|
"0x002F": {
|
||||||
"group": "basic",
|
"group": "basic",
|
||||||
"key": "KC_LEFT_BRACKET",
|
"key": "KC_LEFT_BRACKET",
|
||||||
"label": "]",
|
"label": "[",
|
||||||
"aliases": [
|
"aliases": [
|
||||||
"KC_LBRC"
|
"KC_LBRC"
|
||||||
]
|
]
|
||||||
@ -261,7 +261,7 @@
|
|||||||
"0x0030": {
|
"0x0030": {
|
||||||
"group": "basic",
|
"group": "basic",
|
||||||
"key": "KC_RIGHT_BRACKET",
|
"key": "KC_RIGHT_BRACKET",
|
||||||
"label": "[",
|
"label": "]",
|
||||||
"aliases": [
|
"aliases": [
|
||||||
"KC_RBRC"
|
"KC_RBRC"
|
||||||
]
|
]
|
||||||
@ -1512,4 +1512,4 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
18
data/constants/keycodes/keycodes_0.0.3_quantum.hjson
Normal file
18
data/constants/keycodes/keycodes_0.0.3_quantum.hjson
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"keycodes": {
|
||||||
|
"0x7C79": {
|
||||||
|
"group": "quantum",
|
||||||
|
"key": "QK_REPEAT_KEY",
|
||||||
|
"aliases": [
|
||||||
|
"QK_REP"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"0x7C7A": {
|
||||||
|
"group": "quantum",
|
||||||
|
"key": "QK_ALT_REPEAT_KEY",
|
||||||
|
"aliases": [
|
||||||
|
"QK_AREP"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -33,7 +33,7 @@
|
|||||||
"blok": {
|
"blok": {
|
||||||
"processor": "RP2040",
|
"processor": "RP2040",
|
||||||
"bootloader": "rp2040",
|
"bootloader": "rp2040",
|
||||||
"board": "QMK_PM2040"
|
"board": "QMK_BLOK"
|
||||||
},
|
},
|
||||||
"michi": {
|
"michi": {
|
||||||
"processor": "RP2040",
|
"processor": "RP2040",
|
||||||
@ -74,6 +74,11 @@
|
|||||||
"processor": "RP2040",
|
"processor": "RP2040",
|
||||||
"bootloader": "rp2040",
|
"bootloader": "rp2040",
|
||||||
"board": "QMK_PM2040"
|
"board": "QMK_PM2040"
|
||||||
|
},
|
||||||
|
"liatris": {
|
||||||
|
"processor": "RP2040",
|
||||||
|
"bootloader": "rp2040",
|
||||||
|
"board": "QMK_PM2040"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,95 +10,129 @@
|
|||||||
// deprecated: Default `false`. Set to `true` to turn on warning when a value exists
|
// deprecated: Default `false`. Set to `true` to turn on warning when a value exists
|
||||||
// invalid: Default `false`. Set to `true` to generate errors when a value exists
|
// invalid: Default `false`. Set to `true` to generate errors when a value exists
|
||||||
// replace_with: use with a key marked deprecated or invalid to designate a replacement
|
// replace_with: use with a key marked deprecated or invalid to designate a replacement
|
||||||
|
|
||||||
|
// APA102
|
||||||
|
"APA102_CI_PIN": {"info_key": "apa102.clock_pin"},
|
||||||
|
"APA102_DEFAULT_BRIGHTNESS": {"info_key": "apa102.default_brightness", "value_type": "int"},
|
||||||
|
"APA102_DI_PIN": {"info_key": "apa102.data_pin"},
|
||||||
|
|
||||||
|
// Audio
|
||||||
"AUDIO_VOICES": {"info_key": "audio.voices", "value_type": "bool"},
|
"AUDIO_VOICES": {"info_key": "audio.voices", "value_type": "bool"},
|
||||||
|
"SENDSTRING_BELL": {"info_key": "audio.macro_beep", "value_type": "bool"},
|
||||||
|
|
||||||
|
// Backlight
|
||||||
"BACKLIGHT_BREATHING": {"info_key": "backlight.breathing", "value_type": "bool"},
|
"BACKLIGHT_BREATHING": {"info_key": "backlight.breathing", "value_type": "bool"},
|
||||||
"BREATHING_PERIOD": {"info_key": "backlight.breathing_period", "value_type": "int"},
|
|
||||||
"BACKLIGHT_CAPS_LOCK": {"info_key": "backlight.as_caps_lock", "value_type": "bool"},
|
"BACKLIGHT_CAPS_LOCK": {"info_key": "backlight.as_caps_lock", "value_type": "bool"},
|
||||||
"BACKLIGHT_LEVELS": {"info_key": "backlight.levels", "value_type": "int"},
|
"BACKLIGHT_LEVELS": {"info_key": "backlight.levels", "value_type": "int"},
|
||||||
"BACKLIGHT_LIMIT_VAL": {"info_key": "backlight.max_brightness", "value_type": "int"},
|
"BACKLIGHT_LIMIT_VAL": {"info_key": "backlight.max_brightness", "value_type": "int"},
|
||||||
"BACKLIGHT_ON_STATE": {"info_key": "backlight.on_state", "value_type": "int"},
|
"BACKLIGHT_ON_STATE": {"info_key": "backlight.on_state", "value_type": "int"},
|
||||||
"BACKLIGHT_PIN": {"info_key": "backlight.pin"},
|
"BACKLIGHT_PIN": {"info_key": "backlight.pin"},
|
||||||
"BACKLIGHT_PINS": {"info_key": "backlight.pins", "value_type": "array"},
|
"BACKLIGHT_PINS": {"info_key": "backlight.pins", "value_type": "array"},
|
||||||
"BOOTMAGIC_LITE_ROW": {"info_key": "bootmagic.matrix.0", "value_type": "int"},
|
"BREATHING_PERIOD": {"info_key": "backlight.breathing_period", "value_type": "int"},
|
||||||
|
|
||||||
|
// Bootmagic
|
||||||
"BOOTMAGIC_LITE_COLUMN": {"info_key": "bootmagic.matrix.1", "value_type": "int"},
|
"BOOTMAGIC_LITE_COLUMN": {"info_key": "bootmagic.matrix.1", "value_type": "int"},
|
||||||
"BOOTMAGIC_LITE_ROW_RIGHT": {"info_key": "split.bootmagic.matrix.0", "value_type": "int"},
|
|
||||||
"BOOTMAGIC_LITE_COLUMN_RIGHT": {"info_key": "split.bootmagic.matrix.1", "value_type": "int"},
|
"BOOTMAGIC_LITE_COLUMN_RIGHT": {"info_key": "split.bootmagic.matrix.1", "value_type": "int"},
|
||||||
|
"BOOTMAGIC_LITE_ROW": {"info_key": "bootmagic.matrix.0", "value_type": "int"},
|
||||||
|
"BOOTMAGIC_LITE_ROW_RIGHT": {"info_key": "split.bootmagic.matrix.0", "value_type": "int"},
|
||||||
|
|
||||||
|
// Caps Word
|
||||||
"BOTH_SHIFTS_TURNS_ON_CAPS_WORD": {"info_key": "caps_word.both_shifts_turns_on", "value_type": "bool"},
|
"BOTH_SHIFTS_TURNS_ON_CAPS_WORD": {"info_key": "caps_word.both_shifts_turns_on", "value_type": "bool"},
|
||||||
"CAPS_WORD_IDLE_TIMEOUT": {"info_key": "caps_word.idle_timeout", "value_type": "int"},
|
"CAPS_WORD_IDLE_TIMEOUT": {"info_key": "caps_word.idle_timeout", "value_type": "int"},
|
||||||
"COMBO_COUNT": {"info_key": "combo.count", "value_type": "int"},
|
"CAPS_WORD_INVERT_ON_SHIFT": {"info_key": "caps_word.invert_on_shift", "value_type": "bool"},
|
||||||
"COMBO_TERM": {"info_key": "combo.term", "value_type": "int"},
|
|
||||||
"DEBOUNCE": {"info_key": "debounce", "value_type": "int"},
|
|
||||||
"DIODE_DIRECTION": {"info_key": "diode_direction"},
|
|
||||||
"DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD": {"info_key": "caps_word.double_tap_shift_turns_on", "value_type": "bool"},
|
"DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD": {"info_key": "caps_word.double_tap_shift_turns_on", "value_type": "bool"},
|
||||||
"FORCE_NKRO": {"info_key": "usb.force_nkro", "value_type": "bool"},
|
|
||||||
|
// Combos
|
||||||
|
"COMBO_TERM": {"info_key": "combo.term", "value_type": "int"},
|
||||||
|
|
||||||
|
// Dynamic Keymap
|
||||||
"DYNAMIC_KEYMAP_EEPROM_MAX_ADDR": {"info_key": "dynamic_keymap.eeprom_max_addr", "value_type": "int"},
|
"DYNAMIC_KEYMAP_EEPROM_MAX_ADDR": {"info_key": "dynamic_keymap.eeprom_max_addr", "value_type": "int"},
|
||||||
"DYNAMIC_KEYMAP_LAYER_COUNT": {"info_key": "dynamic_keymap.layer_count", "value_type": "int"},
|
"DYNAMIC_KEYMAP_LAYER_COUNT": {"info_key": "dynamic_keymap.layer_count", "value_type": "int"},
|
||||||
"HOLD_ON_OTHER_KEY_PRESS": {"info_key": "tapping.hold_on_other_key_press", "value_type": "bool"},
|
|
||||||
"HOLD_ON_OTHER_KEY_PRESS_PER_KEY": {"info_key": "tapping.hold_on_other_key_press_per_key", "value_type": "bool"},
|
// Indicators
|
||||||
"LAYOUTS": {"info_key": "layout_aliases", "value_type": "mapping"},
|
|
||||||
"LEADER_PER_KEY_TIMING": {"info_key": "leader_key.timing", "value_type": "bool"},
|
|
||||||
"LEADER_KEY_STRICT_KEY_PROCESSING": {"info_key": "leader_key.strict_processing", "value_type": "bool"},
|
|
||||||
"LEADER_TIMEOUT": {"info_key": "leader_key.timeout", "value_type": "int"},
|
|
||||||
"LED_CAPS_LOCK_PIN": {"info_key": "indicators.caps_lock"},
|
"LED_CAPS_LOCK_PIN": {"info_key": "indicators.caps_lock"},
|
||||||
"LED_NUM_LOCK_PIN": {"info_key": "indicators.num_lock"},
|
"LED_NUM_LOCK_PIN": {"info_key": "indicators.num_lock"},
|
||||||
"LED_SCROLL_LOCK_PIN": {"info_key": "indicators.scroll_lock"},
|
"LED_SCROLL_LOCK_PIN": {"info_key": "indicators.scroll_lock"},
|
||||||
"LED_COMPOSE_PIN": {"info_key": "indicators.compose"},
|
"LED_COMPOSE_PIN": {"info_key": "indicators.compose"},
|
||||||
"LED_KANA_PIN": {"info_key": "indicators.kana"},
|
"LED_KANA_PIN": {"info_key": "indicators.kana"},
|
||||||
"LED_PIN_ON_STATE": {"info_key": "indicators.on_state", "value_type": "int"},
|
"LED_PIN_ON_STATE": {"info_key": "indicators.on_state", "value_type": "int"},
|
||||||
|
|
||||||
|
// Leader Key
|
||||||
|
"LEADER_PER_KEY_TIMING": {"info_key": "leader_key.timing", "value_type": "bool"},
|
||||||
|
"LEADER_KEY_STRICT_KEY_PROCESSING": {"info_key": "leader_key.strict_processing", "value_type": "bool"},
|
||||||
|
"LEADER_TIMEOUT": {"info_key": "leader_key.timeout", "value_type": "int"},
|
||||||
|
|
||||||
|
// LED Matrix
|
||||||
"LED_MATRIX_CENTER": {"info_key": "led_matrix.center_point", "value_type": "array.int"},
|
"LED_MATRIX_CENTER": {"info_key": "led_matrix.center_point", "value_type": "array.int"},
|
||||||
|
"LED_MATRIX_HUE_STEP": {"info_key": "led_matrix.hue_steps", "value_type": "int"},
|
||||||
"LED_MATRIX_MAXIMUM_BRIGHTNESS": {"info_key": "led_matrix.max_brightness", "value_type": "int"},
|
"LED_MATRIX_MAXIMUM_BRIGHTNESS": {"info_key": "led_matrix.max_brightness", "value_type": "int"},
|
||||||
|
"LED_MATRIX_SAT_STEP": {"info_key": "led_matrix.sat_steps", "value_type": "int"},
|
||||||
|
"LED_MATRIX_SPD_STEP": {"info_key": "led_matrix.speed_steps", "value_type": "int"},
|
||||||
"LED_MATRIX_SPLIT": {"info_key": "led_matrix.split_count", "value_type": "array.int"},
|
"LED_MATRIX_SPLIT": {"info_key": "led_matrix.split_count", "value_type": "array.int"},
|
||||||
"LED_MATRIX_TIMEOUT": {"info_key": "led_matrix.timeout", "value_type": "int"},
|
"LED_MATRIX_TIMEOUT": {"info_key": "led_matrix.timeout", "value_type": "int"},
|
||||||
"LED_MATRIX_HUE_STEP": {"info_key": "led_matrix.hue_steps", "value_type": "int"},
|
|
||||||
"LED_MATRIX_SAT_STEP": {"info_key": "led_matrix.sat_steps", "value_type": "int"},
|
|
||||||
"LED_MATRIX_VAL_STEP": {"info_key": "led_matrix.val_steps", "value_type": "int"},
|
"LED_MATRIX_VAL_STEP": {"info_key": "led_matrix.val_steps", "value_type": "int"},
|
||||||
"LED_MATRIX_SPD_STEP": {"info_key": "led_matrix.speed_steps", "value_type": "int"},
|
|
||||||
|
// LUFA Bootloader
|
||||||
|
"QMK_ESC_INPUT": {"info_key": "qmk_lufa_bootloader.esc_input"},
|
||||||
|
"QMK_ESC_OUTPUT": {"info_key": "qmk_lufa_bootloader.esc_output"},
|
||||||
|
"QMK_LED": {"info_key": "qmk_lufa_bootloader.led"},
|
||||||
|
"QMK_SPEAKER": {"info_key": "qmk_lufa_bootloader.speaker"},
|
||||||
|
|
||||||
|
// Matrix
|
||||||
|
"DEBOUNCE": {"info_key": "debounce", "value_type": "int"},
|
||||||
|
"DIODE_DIRECTION": {"info_key": "diode_direction"},
|
||||||
"MATRIX_HAS_GHOST": {"info_key": "matrix_pins.ghost", "value_type": "bool"},
|
"MATRIX_HAS_GHOST": {"info_key": "matrix_pins.ghost", "value_type": "bool"},
|
||||||
"MATRIX_INPUT_PRESSED_STATE": {"info_key": "matrix_pins.input_pressed_state", "value_type": "int"},
|
"MATRIX_INPUT_PRESSED_STATE": {"info_key": "matrix_pins.input_pressed_state", "value_type": "int"},
|
||||||
"MATRIX_IO_DELAY": {"info_key": "matrix_pins.io_delay", "value_type": "int"},
|
"MATRIX_IO_DELAY": {"info_key": "matrix_pins.io_delay", "value_type": "int"},
|
||||||
|
|
||||||
|
// Mouse Keys
|
||||||
"MOUSEKEY_DELAY": {"info_key": "mousekey.delay", "value_type": "int"},
|
"MOUSEKEY_DELAY": {"info_key": "mousekey.delay", "value_type": "int"},
|
||||||
"MOUSEKEY_INTERVAL": {"info_key": "mousekey.interval", "value_type": "int"},
|
"MOUSEKEY_INTERVAL": {"info_key": "mousekey.interval", "value_type": "int"},
|
||||||
"MOUSEKEY_MAX_SPEED": {"info_key": "mousekey.max_speed", "value_type": "int"},
|
"MOUSEKEY_MAX_SPEED": {"info_key": "mousekey.max_speed", "value_type": "int"},
|
||||||
"MOUSEKEY_TIME_TO_MAX": {"info_key": "mousekey.time_to_max", "value_type": "int"},
|
"MOUSEKEY_TIME_TO_MAX": {"info_key": "mousekey.time_to_max", "value_type": "int"},
|
||||||
"MOUSEKEY_WHEEL_DELAY": {"info_key": "mousekey.wheel_delay", "value_type": "int"},
|
"MOUSEKEY_WHEEL_DELAY": {"info_key": "mousekey.wheel_delay", "value_type": "int"},
|
||||||
|
|
||||||
|
// One Shot
|
||||||
"ONESHOT_TIMEOUT": {"info_key": "oneshot.timeout", "value_type": "int"},
|
"ONESHOT_TIMEOUT": {"info_key": "oneshot.timeout", "value_type": "int"},
|
||||||
"ONESHOT_TAP_TOGGLE": {"info_key": "oneshot.tap_toggle", "value_type": "int"},
|
"ONESHOT_TAP_TOGGLE": {"info_key": "oneshot.tap_toggle", "value_type": "int"},
|
||||||
"PERMISSIVE_HOLD": {"info_key": "tapping.permissive_hold", "value_type": "bool"},
|
|
||||||
"PERMISSIVE_HOLD_PER_KEY": {"info_key": "tapping.permissive_hold_per_key", "value_type": "bool"},
|
// PS/2
|
||||||
"PS2_CLOCK_PIN": {"info_key": "ps2.clock_pin"},
|
"PS2_CLOCK_PIN": {"info_key": "ps2.clock_pin"},
|
||||||
"PS2_DATA_PIN": {"info_key": "ps2.data_pin"},
|
"PS2_DATA_PIN": {"info_key": "ps2.data_pin"},
|
||||||
"RETRO_TAPPING": {"info_key": "tapping.retro", "value_type": "bool"},
|
|
||||||
"RETRO_TAPPING_PER_KEY": {"info_key": "tapping.retro_per_key", "value_type": "bool"},
|
// RGB Matrix
|
||||||
"RGB_DI_PIN": {"info_key": "rgblight.pin"},
|
"RGB_MATRIX_CENTER": {"info_key": "rgb_matrix.center_point", "value_type": "array.int"},
|
||||||
|
"RGB_MATRIX_HUE_STEP": {"info_key": "rgb_matrix.hue_steps", "value_type": "int"},
|
||||||
|
"RGB_MATRIX_MAXIMUM_BRIGHTNESS": {"info_key": "rgb_matrix.max_brightness", "value_type": "int"},
|
||||||
|
"RGB_MATRIX_SAT_STEP": {"info_key": "rgb_matrix.sat_steps", "value_type": "int"},
|
||||||
|
"RGB_MATRIX_SPD_STEP": {"info_key": "rgb_matrix.speed_steps", "value_type": "int"},
|
||||||
|
"RGB_MATRIX_SPLIT": {"info_key": "rgb_matrix.split_count", "value_type": "array.int"},
|
||||||
|
"RGB_MATRIX_TIMEOUT": {"info_key": "rgb_matrix.timeout", "value_type": "int"},
|
||||||
|
"RGB_MATRIX_VAL_STEP": {"info_key": "rgb_matrix.val_steps", "value_type": "int"},
|
||||||
|
|
||||||
|
// RGBLight
|
||||||
"RGBLED_NUM": {"info_key": "rgblight.led_count", "value_type": "int"},
|
"RGBLED_NUM": {"info_key": "rgblight.led_count", "value_type": "int"},
|
||||||
"RGBLED_SPLIT": {"info_key": "rgblight.split_count", "value_type": "array.int"},
|
"RGBLED_SPLIT": {"info_key": "rgblight.split_count", "value_type": "array.int"},
|
||||||
|
"RGBLIGHT_HUE_STEP": {"info_key": "rgblight.hue_steps", "value_type": "int"},
|
||||||
"RGBLIGHT_LAYER_BLINK": {"info_key": "rgblight.layers.blink", "value_type": "bool"},
|
"RGBLIGHT_LAYER_BLINK": {"info_key": "rgblight.layers.blink", "value_type": "bool"},
|
||||||
"RGBLIGHT_LAYERS": {"info_key": "rgblight.layers.enabled", "value_type": "bool"},
|
"RGBLIGHT_LAYERS": {"info_key": "rgblight.layers.enabled", "value_type": "bool"},
|
||||||
"RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF": {"info_key": "rgblight.layers.override_rgb", "value_type": "bool"},
|
"RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF": {"info_key": "rgblight.layers.override_rgb", "value_type": "bool"},
|
||||||
"RGBLIGHT_LIMIT_VAL": {"info_key": "rgblight.max_brightness", "value_type": "int"},
|
"RGBLIGHT_LIMIT_VAL": {"info_key": "rgblight.max_brightness", "value_type": "int"},
|
||||||
"RGBLIGHT_MAX_LAYERS": {"info_key": "rgblight.layers.max", "value_type": "int"},
|
"RGBLIGHT_MAX_LAYERS": {"info_key": "rgblight.layers.max", "value_type": "int"},
|
||||||
"RGBLIGHT_HUE_STEP": {"info_key": "rgblight.hue_steps", "value_type": "int"},
|
|
||||||
"RGBLIGHT_SAT_STEP": {"info_key": "rgblight.saturation_steps", "value_type": "int"},
|
"RGBLIGHT_SAT_STEP": {"info_key": "rgblight.saturation_steps", "value_type": "int"},
|
||||||
"RGBLIGHT_VAL_STEP": {"info_key": "rgblight.brightness_steps", "value_type": "int"},
|
|
||||||
"RGBLIGHT_SLEEP": {"info_key": "rgblight.sleep", "value_type": "bool"},
|
"RGBLIGHT_SLEEP": {"info_key": "rgblight.sleep", "value_type": "bool"},
|
||||||
"RGBLIGHT_SPLIT": {"info_key": "rgblight.split", "value_type": "bool"},
|
"RGBLIGHT_SPLIT": {"info_key": "rgblight.split", "value_type": "bool"},
|
||||||
"RGB_MATRIX_CENTER": {"info_key": "rgb_matrix.center_point", "value_type": "array.int"},
|
"RGBLIGHT_VAL_STEP": {"info_key": "rgblight.brightness_steps", "value_type": "int"},
|
||||||
"RGB_MATRIX_MAXIMUM_BRIGHTNESS": {"info_key": "rgb_matrix.max_brightness", "value_type": "int"},
|
|
||||||
"RGB_MATRIX_SPLIT": {"info_key": "rgb_matrix.split_count", "value_type": "array.int"},
|
|
||||||
"RGB_MATRIX_TIMEOUT": {"info_key": "rgb_matrix.timeout", "value_type": "int"},
|
|
||||||
"RGB_MATRIX_HUE_STEP": {"info_key": "rgb_matrix.hue_steps", "value_type": "int"},
|
|
||||||
"RGB_MATRIX_SAT_STEP": {"info_key": "rgb_matrix.sat_steps", "value_type": "int"},
|
|
||||||
"RGB_MATRIX_VAL_STEP": {"info_key": "rgb_matrix.val_steps", "value_type": "int"},
|
|
||||||
"RGB_MATRIX_SPD_STEP": {"info_key": "rgb_matrix.speed_steps", "value_type": "int"},
|
|
||||||
"RGBW": {"info_key": "rgblight.rgbw", "value_type": "bool"},
|
"RGBW": {"info_key": "rgblight.rgbw", "value_type": "bool"},
|
||||||
"QMK_ESC_OUTPUT": {"info_key": "qmk_lufa_bootloader.esc_output"},
|
|
||||||
"QMK_ESC_INPUT": {"info_key": "qmk_lufa_bootloader.esc_input"},
|
// Secure
|
||||||
"QMK_LED": {"info_key": "qmk_lufa_bootloader.led"},
|
"SECURE_IDLE_TIMEOUT": {"info_key": "secure.idle_timeout", "value_type": "int"},
|
||||||
"QMK_SPEAKER": {"info_key": "qmk_lufa_bootloader.speaker"},
|
|
||||||
"SECURE_UNLOCK_SEQUENCE": {"info_key": "secure.unlock_sequence", "value_type": "array.array.int", "to_json": false},
|
"SECURE_UNLOCK_SEQUENCE": {"info_key": "secure.unlock_sequence", "value_type": "array.array.int", "to_json": false},
|
||||||
"SECURE_UNLOCK_TIMEOUT": {"info_key": "secure.unlock_timeout", "value_type": "int"},
|
"SECURE_UNLOCK_TIMEOUT": {"info_key": "secure.unlock_timeout", "value_type": "int"},
|
||||||
"SECURE_IDLE_TIMEOUT": {"info_key": "secure.idle_timeout", "value_type": "int"},
|
|
||||||
"SENDSTRING_BELL": {"info_key": "audio.macro_beep", "value_type": "bool"},
|
// Split Keyboard
|
||||||
|
"SOFT_SERIAL_PIN": {"info_key": "split.soft_serial_pin"},
|
||||||
|
"SOFT_SERIAL_SPEED": {"info_key": "split.soft_serial_speed"},
|
||||||
"SPLIT_MODS_ENABLE": {"info_key": "split.transport.sync_modifiers", "value_type": "bool"},
|
"SPLIT_MODS_ENABLE": {"info_key": "split.transport.sync_modifiers", "value_type": "bool"},
|
||||||
"SPLIT_TRANSPORT_MIRROR": {"info_key": "split.transport.sync_matrix_state", "value_type": "bool"},
|
"SPLIT_TRANSPORT_MIRROR": {"info_key": "split.transport.sync_matrix_state", "value_type": "bool"},
|
||||||
"SPLIT_USB_DETECT": {"info_key": "split.usb_detect.enabled", "value_type": "bool"},
|
"SPLIT_USB_DETECT": {"info_key": "split.usb_detect.enabled", "value_type": "bool"},
|
||||||
@ -106,35 +140,53 @@
|
|||||||
"SPLIT_USB_TIMEOUT_POLL": {"info_key": "split.usb_detect.polling_interval", "value_type": "int"},
|
"SPLIT_USB_TIMEOUT_POLL": {"info_key": "split.usb_detect.polling_interval", "value_type": "int"},
|
||||||
"SPLIT_WATCHDOG_ENABLE": {"info_key": "split.transport.watchdog", "value_type": "bool"},
|
"SPLIT_WATCHDOG_ENABLE": {"info_key": "split.transport.watchdog", "value_type": "bool"},
|
||||||
"SPLIT_WATCHDOG_TIMEOUT": {"info_key": "split.transport.watchdog_timeout", "value_type": "int"},
|
"SPLIT_WATCHDOG_TIMEOUT": {"info_key": "split.transport.watchdog_timeout", "value_type": "int"},
|
||||||
"SOFT_SERIAL_PIN": {"info_key": "split.soft_serial_pin"},
|
|
||||||
"SOFT_SERIAL_SPEED": {"info_key": "split.soft_serial_speed"},
|
// Tapping
|
||||||
|
"HOLD_ON_OTHER_KEY_PRESS": {"info_key": "tapping.hold_on_other_key_press", "value_type": "bool"},
|
||||||
|
"HOLD_ON_OTHER_KEY_PRESS_PER_KEY": {"info_key": "tapping.hold_on_other_key_press_per_key", "value_type": "bool"},
|
||||||
|
"PERMISSIVE_HOLD": {"info_key": "tapping.permissive_hold", "value_type": "bool"},
|
||||||
|
"PERMISSIVE_HOLD_PER_KEY": {"info_key": "tapping.permissive_hold_per_key", "value_type": "bool"},
|
||||||
|
"RETRO_TAPPING": {"info_key": "tapping.retro", "value_type": "bool"},
|
||||||
|
"RETRO_TAPPING_PER_KEY": {"info_key": "tapping.retro_per_key", "value_type": "bool"},
|
||||||
"TAP_CODE_DELAY": {"info_key": "qmk.tap_keycode_delay", "value_type": "int"},
|
"TAP_CODE_DELAY": {"info_key": "qmk.tap_keycode_delay", "value_type": "int"},
|
||||||
"TAP_HOLD_CAPS_DELAY": {"info_key": "qmk.tap_capslock_delay", "value_type": "int"},
|
"TAP_HOLD_CAPS_DELAY": {"info_key": "qmk.tap_capslock_delay", "value_type": "int"},
|
||||||
"TAPPING_TERM": {"info_key": "tapping.term", "value_type": "int"},
|
"TAPPING_TERM": {"info_key": "tapping.term", "value_type": "int"},
|
||||||
"TAPPING_TERM_PER_KEY": {"info_key": "tapping.term_per_key", "value_type": "bool"},
|
"TAPPING_TERM_PER_KEY": {"info_key": "tapping.term_per_key", "value_type": "bool"},
|
||||||
"TAPPING_TOGGLE": {"info_key": "tapping.toggle", "value_type": "int"},
|
"TAPPING_TOGGLE": {"info_key": "tapping.toggle", "value_type": "int"},
|
||||||
|
|
||||||
|
// USB
|
||||||
|
"FORCE_NKRO": {"info_key": "usb.force_nkro", "value_type": "bool"},
|
||||||
"USB_MAX_POWER_CONSUMPTION": {"info_key": "usb.max_power", "value_type": "int"},
|
"USB_MAX_POWER_CONSUMPTION": {"info_key": "usb.max_power", "value_type": "int"},
|
||||||
"USB_POLLING_INTERVAL_MS": {"info_key": "usb.polling_interval", "value_type": "int"},
|
"USB_POLLING_INTERVAL_MS": {"info_key": "usb.polling_interval", "value_type": "int"},
|
||||||
"USB_SUSPEND_WAKEUP_DELAY": {"info_key": "usb.suspend_wakeup_delay", "value_type": "int"},
|
"USB_SUSPEND_WAKEUP_DELAY": {"info_key": "usb.suspend_wakeup_delay", "value_type": "int"},
|
||||||
|
|
||||||
|
// WS2812
|
||||||
|
"WS2812_DI_PIN": {"info_key": "ws2812.pin"},
|
||||||
|
"WS2812_I2C_ADDRESS": {"info_key": "ws2812.i2c_address", "value_type": "hex"},
|
||||||
|
"WS2812_I2C_TIMEOUT": {"info_key": "ws2812.i2c_timeout", "value_type": "int"},
|
||||||
|
|
||||||
|
"LAYOUTS": {"info_key": "layout_aliases", "value_type": "mapping"},
|
||||||
|
|
||||||
// Items we want flagged in lint
|
// Items we want flagged in lint
|
||||||
"NO_ACTION_MACRO": {"info_key": "_invalid.no_action_macro", "invalid": true},
|
|
||||||
"NO_ACTION_FUNCTION": {"info_key": "_invalid.no_action_function", "invalid": true},
|
|
||||||
"DESCRIPTION": {"info_key": "_invalid.usb_description", "invalid": true},
|
|
||||||
"DEBOUNCING_DELAY": {"info_key": "_invalid.debouncing_delay", "invalid": true, "replace_with": "DEBOUNCE"},
|
"DEBOUNCING_DELAY": {"info_key": "_invalid.debouncing_delay", "invalid": true, "replace_with": "DEBOUNCE"},
|
||||||
|
"DESCRIPTION": {"info_key": "_invalid.usb_description", "invalid": true},
|
||||||
|
"IGNORE_MOD_TAP_INTERRUPT": {"info_key": "_invalid.ignore_mod_tap_interrupt", "value_type": "bool", "invalid": true},
|
||||||
|
"IGNORE_MOD_TAP_INTERRUPT_PER_KEY": {"info_key": "_invalid.ignore_mod_tap_interrupt_per_key", "invalid": true}
|
||||||
|
"NO_ACTION_FUNCTION": {"info_key": "_invalid.no_action_function", "invalid": true},
|
||||||
|
"NO_ACTION_MACRO": {"info_key": "_invalid.no_action_macro", "invalid": true},
|
||||||
"PREVENT_STUCK_MODIFIERS": {"info_key": "_invalid.prevent_stuck_mods", "invalid": true},
|
"PREVENT_STUCK_MODIFIERS": {"info_key": "_invalid.prevent_stuck_mods", "invalid": true},
|
||||||
"UNUSED_PINS": {"info_key": "_invalid.unused_pins", "deprecated": true},
|
|
||||||
"RGBLIGHT_ANIMATIONS": {"info_key": "_invalid.rgblight.animations.all", "value_type": "bool", "invalid": true},
|
|
||||||
"QMK_KEYS_PER_SCAN": {"info_key": "qmk.keys_per_scan", "value_type": "int", "deprecated": true},
|
"QMK_KEYS_PER_SCAN": {"info_key": "qmk.keys_per_scan", "value_type": "int", "deprecated": true},
|
||||||
|
"RGB_DI_PIN": {"info_key": "rgblight.pin", "invalid": true, "replace_with": "WS2812_DI_PIN or APA102_DI_PIN"},
|
||||||
|
"RGBLIGHT_ANIMATIONS": {"info_key": "_invalid.rgblight.animations.all", "value_type": "bool", "invalid": true},
|
||||||
"TAPPING_FORCE_HOLD": {"info_key": "tapping.force_hold", "value_type": "bool", "deprecated": true},
|
"TAPPING_FORCE_HOLD": {"info_key": "tapping.force_hold", "value_type": "bool", "deprecated": true},
|
||||||
"TAPPING_FORCE_HOLD_PER_KEY": {"info_key": "tapping.force_hold_per_key", "value_type": "bool", "deprecated": true},
|
"TAPPING_FORCE_HOLD_PER_KEY": {"info_key": "tapping.force_hold_per_key", "value_type": "bool", "deprecated": true},
|
||||||
"IGNORE_MOD_TAP_INTERRUPT": {"info_key": "_deprecated.ignore_mod_tap_interrupt", "value_type": "bool", "deprecated": true},
|
"UNUSED_PINS": {"info_key": "_invalid.unused_pins", "deprecated": true},
|
||||||
"IGNORE_MOD_TAP_INTERRUPT_PER_KEY": {"info_key": "_invalid.ignore_mod_tap_interrupt_per_key", "invalid": true}
|
"COMBO_COUNT": {"info_key": "_invalid.combo.count", "invalid": true},
|
||||||
|
|
||||||
// USB params, need to mark as failure when specified in config.h, rather than deprecated
|
// USB params, need to mark as failure when specified in config.h, rather than deprecated
|
||||||
|
"DEVICE_VER": {"info_key": "usb.device_version", "value_type": "bcd_version", "deprecated": true, "replace_with": "`usb.device_version` in info.json"},
|
||||||
|
"MANUFACTURER": {"info_key": "manufacturer", "value_type": "str", "deprecated": true, "replace_with": "`manufacturer` in info.json"},
|
||||||
|
"PRODUCT": {"info_key": "keyboard_name", "warn_duplicate": false, "value_type": "str", "deprecated": true, "replace_with": "`keyboard_name` in info.json"},
|
||||||
"PRODUCT_ID": {"info_key": "usb.pid", "value_type": "hex", "deprecated": true, "replace_with": "`usb.pid` in info.json"},
|
"PRODUCT_ID": {"info_key": "usb.pid", "value_type": "hex", "deprecated": true, "replace_with": "`usb.pid` in info.json"},
|
||||||
"VENDOR_ID": {"info_key": "usb.vid", "value_type": "hex", "deprecated": true, "replace_with": "`usb.vid` in info.json"},
|
"VENDOR_ID": {"info_key": "usb.vid", "value_type": "hex", "deprecated": true, "replace_with": "`usb.vid` in info.json"},
|
||||||
"PRODUCT": {"info_key": "keyboard_name", "warn_duplicate": false, "value_type": "str", "deprecated": true, "replace_with": "`keyboard_name` in info.json"},
|
|
||||||
"MANUFACTURER": {"info_key": "manufacturer", "value_type": "str", "deprecated": true, "replace_with": "`manufacturer` in info.json"},
|
|
||||||
"DEVICE_VER": {"info_key": "usb.device_version", "value_type": "bcd_version", "deprecated": true, "replace_with": "`usb.device_version` in info.json"}
|
|
||||||
}
|
}
|
||||||
|
@ -10,36 +10,38 @@
|
|||||||
// deprecated: Default `false`. Set to `true` to turn on warning when a value exists
|
// deprecated: Default `false`. Set to `true` to turn on warning when a value exists
|
||||||
// invalid: Default `false`. Set to `true` to generate errors when a value exists
|
// invalid: Default `false`. Set to `true` to generate errors when a value exists
|
||||||
// replace_with: use with a key marked deprecated or invalid to designate a replacement
|
// replace_with: use with a key marked deprecated or invalid to designate a replacement
|
||||||
|
|
||||||
|
"BACKLIGHT_DRIVER": {"info_key": "backlight.driver"},
|
||||||
|
"BLUETOOTH_DRIVER": {"info_key": "bluetooth.driver"},
|
||||||
"BOARD": {"info_key": "board"},
|
"BOARD": {"info_key": "board"},
|
||||||
"BOOTLOADER": {"info_key": "bootloader", "warn_duplicate": false},
|
"BOOTLOADER": {"info_key": "bootloader", "warn_duplicate": false},
|
||||||
"BOOTMAGIC_ENABLE": {"info_key": "bootmagic.enabled", "value_type": "bool"},
|
"BOOTMAGIC_ENABLE": {"info_key": "bootmagic.enabled", "value_type": "bool"},
|
||||||
"BLUETOOTH_DRIVER": {"info_key": "bluetooth.driver"},
|
|
||||||
"BACKLIGHT_DRIVER": {"info_key": "backlight.driver"},
|
|
||||||
"CAPS_WORD_ENABLE": {"info_key": "caps_word.enabled", "value_type": "bool"},
|
"CAPS_WORD_ENABLE": {"info_key": "caps_word.enabled", "value_type": "bool"},
|
||||||
"DEBOUNCE_TYPE": {"info_key": "build.debounce_type"},
|
"DEBOUNCE_TYPE": {"info_key": "build.debounce_type"},
|
||||||
|
"EEPROM_DRIVER": {"info_key": "eeprom.driver"},
|
||||||
"ENCODER_ENABLE": {"info_key": "encoder.enabled", "value_type": "bool"},
|
"ENCODER_ENABLE": {"info_key": "encoder.enabled", "value_type": "bool"},
|
||||||
"FIRMWARE_FORMAT": {"info_key": "build.firmware_format"},
|
"FIRMWARE_FORMAT": {"info_key": "build.firmware_format"},
|
||||||
"KEYBOARD_SHARED_EP": {"info_key": "usb.shared_endpoint.keyboard", "value_type": "bool"},
|
"KEYBOARD_SHARED_EP": {"info_key": "usb.shared_endpoint.keyboard", "value_type": "bool"},
|
||||||
"MOUSE_SHARED_EP": {"info_key": "usb.shared_endpoint.mouse", "value_type": "bool"},
|
|
||||||
"LAYOUTS": {"info_key": "community_layouts", "value_type": "list"},
|
"LAYOUTS": {"info_key": "community_layouts", "value_type": "list"},
|
||||||
"LED_MATRIX_DRIVER": {"info_key": "led_matrix.driver"},
|
"LED_MATRIX_DRIVER": {"info_key": "led_matrix.driver"},
|
||||||
"RGB_MATRIX_DRIVER": {"info_key": "rgb_matrix.driver"},
|
|
||||||
"LTO_ENABLE": {"info_key": "build.lto", "value_type": "bool"},
|
"LTO_ENABLE": {"info_key": "build.lto", "value_type": "bool"},
|
||||||
"MCU": {"info_key": "processor", "warn_duplicate": false},
|
"MCU": {"info_key": "processor", "warn_duplicate": false},
|
||||||
|
"MOUSE_SHARED_EP": {"info_key": "usb.shared_endpoint.mouse", "value_type": "bool"},
|
||||||
"MOUSEKEY_ENABLE": {"info_key": "mouse_key.enabled", "value_type": "bool"},
|
"MOUSEKEY_ENABLE": {"info_key": "mouse_key.enabled", "value_type": "bool"},
|
||||||
"NO_USB_STARTUP_CHECK": {"info_key": "usb.no_startup_check", "value_type": "bool"},
|
"NO_USB_STARTUP_CHECK": {"info_key": "usb.no_startup_check", "value_type": "bool"},
|
||||||
"PIN_COMPATIBLE": {"info_key": "pin_compatible"},
|
"PIN_COMPATIBLE": {"info_key": "pin_compatible"},
|
||||||
|
"PLATFORM_KEY": {"info_key": "platform_key", "to_json": false},
|
||||||
|
"PS2_DRIVER": {"info_key": "ps2.driver"},
|
||||||
|
"PS2_ENABLE": {"info_key": "ps2.enabled", "value_type": "bool"},
|
||||||
|
"PS2_MOUSE_ENABLE": {"info_key": "ps2.mouse_enabled", "value_type": "bool"},
|
||||||
|
"RGB_MATRIX_DRIVER": {"info_key": "rgb_matrix.driver"},
|
||||||
"SECURE_ENABLE": {"info_key": "secure.enabled", "value_type": "bool"},
|
"SECURE_ENABLE": {"info_key": "secure.enabled", "value_type": "bool"},
|
||||||
"SPLIT_KEYBOARD": {"info_key": "split.enabled", "value_type": "bool"},
|
"SPLIT_KEYBOARD": {"info_key": "split.enabled", "value_type": "bool"},
|
||||||
"SPLIT_TRANSPORT": {"info_key": "split.transport.protocol", "to_c": false},
|
"SPLIT_TRANSPORT": {"info_key": "split.transport.protocol", "to_c": false},
|
||||||
"WAIT_FOR_USB": {"info_key": "usb.wait_for", "value_type": "bool"},
|
|
||||||
"STENO_ENABLE": {"info_key": "stenography.enabled", "value_type": "bool"},
|
"STENO_ENABLE": {"info_key": "stenography.enabled", "value_type": "bool"},
|
||||||
"STENO_PROTOCOL": {"info_key": "stenography.protocol"},
|
"STENO_PROTOCOL": {"info_key": "stenography.protocol"},
|
||||||
"PS2_ENABLE": {"info_key": "ps2.enabled", "value_type": "bool"},
|
"WAIT_FOR_USB": {"info_key": "usb.wait_for", "value_type": "bool"},
|
||||||
"PS2_MOUSE_ENABLE": {"info_key": "ps2.mouse_enabled", "value_type": "bool"},
|
"WS2812_DRIVER": {"info_key": "ws2812.driver"},
|
||||||
"PS2_DRIVER": {"info_key": "ps2.driver"},
|
|
||||||
|
|
||||||
"PLATFORM_KEY": {"info_key": "platform_key", "to_json": false},
|
|
||||||
|
|
||||||
// Items we want flagged in lint
|
// Items we want flagged in lint
|
||||||
"CTPC": {"info_key": "_deprecated.ctpc", "deprecated": true, "replace_with": "CONVERT_TO=proton_c"},
|
"CTPC": {"info_key": "_deprecated.ctpc", "deprecated": true, "replace_with": "CONVERT_TO=proton_c"},
|
||||||
|
@ -41,6 +41,9 @@
|
|||||||
"angel64": {
|
"angel64": {
|
||||||
"target": "angel64/alpha"
|
"target": "angel64/alpha"
|
||||||
},
|
},
|
||||||
|
"ashpil/modelm_usbc": {
|
||||||
|
"target": "ibm/model_m/ashpil_usbc"
|
||||||
|
},
|
||||||
"at101_blackheart": {
|
"at101_blackheart": {
|
||||||
"target": "viktus/at101_bh"
|
"target": "viktus/at101_bh"
|
||||||
},
|
},
|
||||||
@ -113,6 +116,15 @@
|
|||||||
"cmm_studio/saka68": {
|
"cmm_studio/saka68": {
|
||||||
"target": "cmm_studio/saka68/solder"
|
"target": "cmm_studio/saka68/solder"
|
||||||
},
|
},
|
||||||
|
"converter/modelm101": {
|
||||||
|
"target": "ibm/model_m/teensypp"
|
||||||
|
},
|
||||||
|
"converter/modelm101_teensy2": {
|
||||||
|
"target": "ibm/model_m/teensy2"
|
||||||
|
},
|
||||||
|
"converter/modelm_ssk": {
|
||||||
|
"target": "ibm/model_m_ssk/teensypp_ssk"
|
||||||
|
},
|
||||||
"cospad": {
|
"cospad": {
|
||||||
"target": "kprepublic/cospad"
|
"target": "kprepublic/cospad"
|
||||||
},
|
},
|
||||||
@ -149,6 +161,15 @@
|
|||||||
"durgod/k320": {
|
"durgod/k320": {
|
||||||
"target": "durgod/k3x0/k320"
|
"target": "durgod/k3x0/k320"
|
||||||
},
|
},
|
||||||
|
"durgod/hades": {
|
||||||
|
"target": "durgod/dgk6x/hades_ansi"
|
||||||
|
},
|
||||||
|
"durgod/hades_ansi": {
|
||||||
|
"target": "durgod/dgk6x/hades_ansi"
|
||||||
|
},
|
||||||
|
"durgod/hades_iso": {
|
||||||
|
"target": "durgod/dgk6x/hades_iso"
|
||||||
|
},
|
||||||
"dztech/dz60rgb": {
|
"dztech/dz60rgb": {
|
||||||
"target": "dztech/dz60rgb/v1"
|
"target": "dztech/dz60rgb/v1"
|
||||||
},
|
},
|
||||||
@ -194,6 +215,9 @@
|
|||||||
"handwired/ferris": {
|
"handwired/ferris": {
|
||||||
"target": "ferris/0_1"
|
"target": "ferris/0_1"
|
||||||
},
|
},
|
||||||
|
"handwired/ibm122m": {
|
||||||
|
"target": "ibm/model_m_122/ibm122m"
|
||||||
|
},
|
||||||
"handwired/p1800fl": {
|
"handwired/p1800fl": {
|
||||||
"target": "team0110/p1800fl"
|
"target": "team0110/p1800fl"
|
||||||
},
|
},
|
||||||
@ -248,6 +272,9 @@
|
|||||||
"idobo": {
|
"idobo": {
|
||||||
"target": "idobao/id75"
|
"target": "idobao/id75"
|
||||||
},
|
},
|
||||||
|
"jacky_studio/piggy60": {
|
||||||
|
"target": "jacky_studio/piggy60/rev1"
|
||||||
|
},
|
||||||
"jj40": {
|
"jj40": {
|
||||||
"target": "kprepublic/jj40"
|
"target": "kprepublic/jj40"
|
||||||
},
|
},
|
||||||
@ -260,6 +287,12 @@
|
|||||||
"jones": {
|
"jones": {
|
||||||
"target": "jones/v03_1"
|
"target": "jones/v03_1"
|
||||||
},
|
},
|
||||||
|
"kamigakushi": {
|
||||||
|
"target": "jaykeeb/kamigakushi",
|
||||||
|
"layouts": {
|
||||||
|
"LAYOUT": "LAYOUT_65_ansi_blocker_tsangan"
|
||||||
|
}
|
||||||
|
},
|
||||||
"katana60": {
|
"katana60": {
|
||||||
"target": "rominronin/katana60/rev1"
|
"target": "rominronin/katana60/rev1"
|
||||||
},
|
},
|
||||||
@ -407,6 +440,9 @@
|
|||||||
"montsinger/rebound": {
|
"montsinger/rebound": {
|
||||||
"target": "montsinger/rebound/rev1"
|
"target": "montsinger/rebound/rev1"
|
||||||
},
|
},
|
||||||
|
"mschwingen/modelm": {
|
||||||
|
"target": "ibm/model_m/mschwingen"
|
||||||
|
},
|
||||||
"noxary/268_2": {
|
"noxary/268_2": {
|
||||||
"layouts": {
|
"layouts": {
|
||||||
"LAYOUT": "LAYOUT_65_ansi_blocker"
|
"LAYOUT": "LAYOUT_65_ansi_blocker"
|
||||||
@ -925,6 +961,12 @@
|
|||||||
"m3n3van": {
|
"m3n3van": {
|
||||||
"target": "matthewdias/m3n3van"
|
"target": "matthewdias/m3n3van"
|
||||||
},
|
},
|
||||||
|
"massdrop/thekey": {
|
||||||
|
"target": "drop/thekey/v1"
|
||||||
|
},
|
||||||
|
"massdrop/thekey_v2": {
|
||||||
|
"target": "drop/thekey/v2"
|
||||||
|
},
|
||||||
"mechmini/v1": {
|
"mechmini/v1": {
|
||||||
"target": "mechkeys/mechmini/v1"
|
"target": "mechkeys/mechmini/v1"
|
||||||
},
|
},
|
||||||
@ -1222,6 +1264,9 @@
|
|||||||
"treadstone48/rev2": {
|
"treadstone48/rev2": {
|
||||||
"target": "marksard/treadstone48/rev2"
|
"target": "marksard/treadstone48/rev2"
|
||||||
},
|
},
|
||||||
|
"tronguylabs/m122_3270": {
|
||||||
|
"target": "ibm/model_m_122/m122_3270"
|
||||||
|
},
|
||||||
"ua62": {
|
"ua62": {
|
||||||
"target": "nacly/ua62"
|
"target": "nacly/ua62"
|
||||||
},
|
},
|
||||||
@ -1282,6 +1327,9 @@
|
|||||||
"ymdk_np21": {
|
"ymdk_np21": {
|
||||||
"target": "ymdk/np21"
|
"target": "ymdk/np21"
|
||||||
},
|
},
|
||||||
|
"yugo_m/model_m_101": {
|
||||||
|
"target": "ibm/model_m/yugo_m"
|
||||||
|
},
|
||||||
"yurei": {
|
"yurei": {
|
||||||
"target": "kkatano/yurei"
|
"target": "kkatano/yurei"
|
||||||
},
|
},
|
||||||
|
@ -71,6 +71,38 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^[0-9a-z][0-9a-z_/]*$"
|
"pattern": "^[0-9a-z][0-9a-z_/]*$"
|
||||||
},
|
},
|
||||||
|
"keycode": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 2,
|
||||||
|
"maxLength": 50,
|
||||||
|
"pattern": "^[A-Z][A-Zs_0-9]*$"
|
||||||
|
},
|
||||||
|
"keycode_short": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 2,
|
||||||
|
"maxLength": 7,
|
||||||
|
"pattern": "^[A-Z][A-Zs_0-9]*$"
|
||||||
|
},
|
||||||
|
"keycode_decl": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"key"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"key": {"$ref": "#/keycode"},
|
||||||
|
"label": {"$ref": "#/text_identifier"},
|
||||||
|
"aliases": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"items": {"$ref": "#/keycode_short"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"keycode_decl_array": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1
|
||||||
|
"items": {"$ref": "#/keycode_decl"}
|
||||||
|
},
|
||||||
"mcu_pin_array": {
|
"mcu_pin_array": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {"$ref": "#/mcu_pin"}
|
"items": {"$ref": "#/mcu_pin"}
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
},
|
},
|
||||||
"development_board": {
|
"development_board": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["promicro", "elite_c", "elite_pi", "proton_c", "kb2040", "promicro_rp2040", "blok", "michi", "bit_c_pro", "stemcell", "bluepill", "blackpill_f401", "blackpill_f411", "bonsai_c4", "helios"]
|
"enum": ["promicro", "elite_c", "elite_pi", "proton_c", "kb2040", "promicro_rp2040", "blok", "michi", "bit_c_pro", "stemcell", "bluepill", "blackpill_f401", "blackpill_f411", "bonsai_c4", "helios", "liatris"]
|
||||||
},
|
},
|
||||||
"pin_compatible": {
|
"pin_compatible": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -96,6 +96,19 @@
|
|||||||
"unknown"
|
"unknown"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"apa102": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"data_pin": {"$ref": "qmk.definitions.v1#/mcu_pin"},
|
||||||
|
"clock_pin": {"$ref": "qmk.definitions.v1#/mcu_pin"},
|
||||||
|
"default_brightness": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0,
|
||||||
|
"maximum": 31
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"audio": {
|
"audio": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
@ -133,7 +146,7 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"driver": {
|
"driver": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["BluefruitLE", "RN42"]
|
"enum": ["BluefruitLE", "RN42", "custom"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -215,7 +228,8 @@
|
|||||||
"enabled": {"type": "boolean"},
|
"enabled": {"type": "boolean"},
|
||||||
"both_shifts_turns_on": {"type": "boolean"},
|
"both_shifts_turns_on": {"type": "boolean"},
|
||||||
"double_tap_shift_turns_on": {"type": "boolean"},
|
"double_tap_shift_turns_on": {"type": "boolean"},
|
||||||
"idle_timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"}
|
"idle_timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"},
|
||||||
|
"invert_on_shift": {"type": "boolean"}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"combo": {
|
"combo": {
|
||||||
@ -229,6 +243,11 @@
|
|||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {"$ref": "qmk.definitions.v1#/filename"}
|
"items": {"$ref": "qmk.definitions.v1#/filename"}
|
||||||
},
|
},
|
||||||
|
"eeprom": {
|
||||||
|
"properties": {
|
||||||
|
"driver": {"type": "string"}
|
||||||
|
}
|
||||||
|
},
|
||||||
"encoder": {
|
"encoder": {
|
||||||
"$ref": "#/definitions/encoder_config",
|
"$ref": "#/definitions/encoder_config",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -247,6 +266,7 @@
|
|||||||
"on_state": {"$ref": "qmk.definitions.v1#/bit"}
|
"on_state": {"$ref": "qmk.definitions.v1#/bit"}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"keycodes": {"$ref": "qmk.definitions.v1#/keycode_decl_array"},
|
||||||
"layout_aliases": {
|
"layout_aliases": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": {"$ref": "qmk.definitions.v1#/layout_macro"}
|
"additionalProperties": {"$ref": "qmk.definitions.v1#/layout_macro"}
|
||||||
@ -473,7 +493,10 @@
|
|||||||
},
|
},
|
||||||
"led_count": {"$ref": "qmk.definitions.v1#/unsigned_int"},
|
"led_count": {"$ref": "qmk.definitions.v1#/unsigned_int"},
|
||||||
"max_brightness": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
|
"max_brightness": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
|
||||||
"pin": {"$ref": "qmk.definitions.v1#/mcu_pin"},
|
"pin": {
|
||||||
|
"$ref": "qmk.definitions.v1#/mcu_pin",
|
||||||
|
"$comment": "Deprecated: use ws2812.pin instead"
|
||||||
|
},
|
||||||
"rgbw": {"type": "boolean"},
|
"rgbw": {"type": "boolean"},
|
||||||
"saturation_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
|
"saturation_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
|
||||||
"sleep": {"type": "boolean"},
|
"sleep": {"type": "boolean"},
|
||||||
@ -687,6 +710,19 @@
|
|||||||
"led": {"$ref": "qmk.definitions.v1#/mcu_pin"},
|
"led": {"$ref": "qmk.definitions.v1#/mcu_pin"},
|
||||||
"speaker": {"$ref": "qmk.definitions.v1#/mcu_pin"}
|
"speaker": {"$ref": "qmk.definitions.v1#/mcu_pin"}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"ws2812": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"driver": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["bitbang", "custom", "i2c", "pwm", "spi", "vendor"]
|
||||||
|
},
|
||||||
|
"pin": {"$ref": "qmk.definitions.v1#/mcu_pin"},
|
||||||
|
"i2c_address": {"$ref": "qmk.definitions.v1#/hex_number_2d"},
|
||||||
|
"i2c_timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 2,
|
"minLength": 2,
|
||||||
"maxLength": 50,
|
"maxLength": 50,
|
||||||
"pattern": "^[A-Zs_0-9]*$"
|
"pattern": "^[A-Z][A-Zs_0-9]*$"
|
||||||
},
|
|
||||||
"hex_number_4d": {
|
|
||||||
"type": "string",
|
|
||||||
"pattern": "^0x[0-9A-F]{4}$"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -34,10 +30,10 @@
|
|||||||
"keycodes": {
|
"keycodes": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"propertyNames": {
|
"propertyNames": {
|
||||||
"$ref": "#/definitions/hex_number_4d"
|
"$ref": "qmk.definitions.v1#/hex_number_4d"
|
||||||
},
|
},
|
||||||
"additionalProperties": {
|
"additionalProperties": {
|
||||||
"type": "object",
|
"type": "object", // use 'qmk.definitions.v1#/keycode_decl' when problem keycodes are removed
|
||||||
"required": [
|
"required": [
|
||||||
"key"
|
"key"
|
||||||
],
|
],
|
||||||
|
@ -67,6 +67,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"keycodes": {"$ref": "qmk.definitions.v1#/keycode_decl_array"},
|
||||||
"config": {"$ref": "qmk.keyboard.v1"},
|
"config": {"$ref": "qmk.keyboard.v1"},
|
||||||
"notes": {
|
"notes": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
551
docs/ChangeLog/20230528.md
Normal file
551
docs/ChangeLog/20230528.md
Normal file
@ -0,0 +1,551 @@
|
|||||||
|
# QMK Breaking Changes - 2023 May 28 Changelog
|
||||||
|
|
||||||
|
## Notable Changes :id=notable-changes
|
||||||
|
|
||||||
|
As per last breaking changes cycle, there has been _a lot_ of emphasis on behind-the-scenes changes, mainly around migration of configurables into `info.json` files, cleanup of `info.json` files, additional layout definitions for keyboards, adding support for general community layouts to keyboards, as well as addressing technical debt.
|
||||||
|
|
||||||
|
Of note for keyboard designers:
|
||||||
|
|
||||||
|
* Layout and matrix definitions in `info.json` are now _mandatory_ for merge into QMK.
|
||||||
|
* Layout macros in `<yourkeyboard>.h` are no longer accepted into QMK Firmware.
|
||||||
|
* Existing keyboards have been meticulously converted by the QMK collaborators
|
||||||
|
* Layouts missing from keyboard definitions have been added in the process
|
||||||
|
* Keys within layouts should not specify `"w":1` or `"h":1` if the key size is 1 -- `w`/`h` should only be present for sizes other than 1
|
||||||
|
* `config_common.h` has been removed and should not be present anywhere in your keyboard code.
|
||||||
|
* `RGB_DI_PIN` will now cause an error during build:
|
||||||
|
* For WS2812-like LEDs, this should be moved to `info.json`: `"ws2812": { "pin": "xxx" }`
|
||||||
|
* For APA102 LEDs, this should be moved to `info.json`: `"apa102": { "data_pin": "xxx" }`
|
||||||
|
* Other mandatory data-driven changes should be automatically flagged during build
|
||||||
|
* Keymaps with `encoder_map` should now have the following change made:
|
||||||
|
* `encoder_map[][NUM_ENCODERS][2]` => `encoder_map[][NUM_ENCODERS][NUM_DIRECTIONS]`
|
||||||
|
* Users assumed the `2` referred to the number of encoders, rather than the number of directions (which is always 2)
|
||||||
|
|
||||||
|
### Repeat last key ([#19700](https://github.com/qmk/qmk_firmware/pull/19700)) :id=repeat-last-key
|
||||||
|
|
||||||
|
A new pair of keys has been added to QMK -- namely `QK_REPEAT_KEY` and `QK_ALT_REPEAT_KEY` (shortened: `QK_REP`/`QK_AREP`). These allow you to repeat the last key pressed, or in the case of the alternate key, press the "opposite" of the last key. For example, if you press `KC_LEFT`, pressing `QK_REPEAT_KEY` afterwards repeats `KC_LEFT`, but pressing `QK_ALT_REPEAT_KEY` instead sends `KC_RIGHT`.
|
||||||
|
|
||||||
|
The full list of default alternate keys is available on the [Repeat Key](feature_repeat_key.md) documentation.
|
||||||
|
|
||||||
|
To enable these keys, in your keymap's `rules.mk`, add:
|
||||||
|
|
||||||
|
```make
|
||||||
|
REPEAT_KEY_ENABLE = yes
|
||||||
|
```
|
||||||
|
|
||||||
|
...and add them to your keymap.
|
||||||
|
|
||||||
|
### User callback for pre process record ([#20584](https://github.com/qmk/qmk_firmware/pull/20584)) :id=user-callback-for-pre-process-record
|
||||||
|
|
||||||
|
Two new boolean callback functions, `pre_process_record_kb` and `pre_process_record_user`, have been added. They are called at the beginning of `process_record`, right before `process_combo`.
|
||||||
|
|
||||||
|
Similar to existing `*_kb` and `*_user` callback functions, returning `false` will halt further processing of key events. The `pre_process_record_user` function will allow user space opportunity to handle or capture an input before it undergoes quantum processing. For example, while action tapping is still resolving the tap or hold output of a mod-tap key, `pre_process_record_user` can capture the next key record of an input event that follows. That key record can be used to influence the [decision of the mod-tap](https://docs.qmk.fm/#/tap_hold) key that is currently undergoing quantum processing.
|
||||||
|
|
||||||
|
### Consolidate modelm ([#14996](https://github.com/qmk/qmk_firmware/pull/14996) :id=consolidate-modelm
|
||||||
|
|
||||||
|
Several build targets for the IBM Model M were cluttered in different folders. The maintainers of several Model M replacement controller projects agreed to consolidate them under one common folder.
|
||||||
|
|
||||||
|
The list of all moved keyboard locations is listed [below](20230528.md#updated-keyboard-codebases).
|
||||||
|
|
||||||
|
## Changes Requiring User Action :id=changes-requiring-user-action
|
||||||
|
|
||||||
|
### `IGNORE_MOD_TAP_INTERRUPT` behaviour changes ([#20211](https://github.com/qmk/qmk_firmware/pull/20211)) :id=i-m-t-i
|
||||||
|
|
||||||
|
Following up from the last breaking changes cycle, `IGNORE_MOD_TAP_INTERRUPT` has been removed and if present in keymap code, will now fail to build. The previous functionality for `IGNORE_MOD_TAP_INTERRUPT` is now default, and should you wish to revert to the old behaviour, you can use `HOLD_ON_OTHER_KEY_PRESS` instead.
|
||||||
|
|
||||||
|
For more information, you are invited to read the section on [HOLD_ON_OTHER_KEY_PRESS](tap_hold.md#hold-on-other-key-press) in the page on [Tap-Hold configuration options](tap_hold.md).
|
||||||
|
|
||||||
|
### Updated Keyboard Codebases :id=updated-keyboard-codebases
|
||||||
|
|
||||||
|
| Old Keyboard Name | New Keyboard Name |
|
||||||
|
|---------------------------------|-------------------------------------|
|
||||||
|
| ashpil/modelm_usbc | ibm/model_m/ashpil_usbc |
|
||||||
|
| binepad/bn009r2 | binepad/bn009/r2 |
|
||||||
|
| converter/modelm101 | ibm/model_m/teensypp |
|
||||||
|
| converter/modelm101_teensy2 | ibm/model_m/teensy2 |
|
||||||
|
| converter/modelm_ssk | ibm/model_m_ssk/teensypp_ssk |
|
||||||
|
| durgod/dgk6x/hades | durgod/dgk6x/hades_ansi |
|
||||||
|
| handwired/ibm122m | ibm/model_m_122/ibm122m |
|
||||||
|
| jacky_studio/piggy60/hotswap | jacky_studio/piggy60/rev1/hotswap |
|
||||||
|
| jacky_studio/piggy60/solder | jacky_studio/piggy60/rev1/solder |
|
||||||
|
| kamigakushi | jaykeeb/kamigakushi |
|
||||||
|
| massdrop/thekey | drop/thekey/v1 |
|
||||||
|
| massdrop/thekey_v2 | drop/thekey/v2 |
|
||||||
|
| mschwingen/modelm | ibm/model_m/mschwingen |
|
||||||
|
| tronguylabs/m122_3270 | ibm/model_m_122/m122_3270 |
|
||||||
|
| tronguylabs/m122_3270/blackpill | ibm/model_m_122/m122_3270/blackpill |
|
||||||
|
| tronguylabs/m122_3270/bluepill | ibm/model_m_122/m122_3270/bluepill |
|
||||||
|
| tronguylabs/m122_3270/teensy | ibm/model_m_122/m122_3270/teensy |
|
||||||
|
| yugo_m/model_m_101 | ibm/model_m/yugo_m |
|
||||||
|
|
||||||
|
## Notable core changes :id=notable-core
|
||||||
|
|
||||||
|
### Encoder functionality fallback ([#20320](https://github.com/qmk/qmk_firmware/pull/20320)) :id=encoder-functionality-fallback
|
||||||
|
|
||||||
|
For keyboards who have not yet been migrated to encoder map, a default set of encoder functionality is now enabled, gracefully degrading functionality depending on which flags are enabled by the keyboard:
|
||||||
|
|
||||||
|
* If `EXTRAKEY_ENABLE` is enabled by the keyboard, the encoder will be mapped to `KC_VOLU`/`KC_VOLD`
|
||||||
|
* If `MOUSEKEY_ENABLE` is enabled by the keyboard, the encoder will be mapped to `KC_MS_WH_UP`/`KC_MS_WH_DOWN`
|
||||||
|
* Otherwise, `KC_PGDN`/`KC_PGUP` will be used
|
||||||
|
|
||||||
|
Additionally, this ensures that builds on QMK Configurator produce some sort of usable encoder mapping.
|
||||||
|
|
||||||
|
### OLED Driver Improvements ([#20331](https://github.com/qmk/qmk_firmware/pull/20331)) :id=oled-driver-improvements
|
||||||
|
|
||||||
|
The "classic" OLED driver picked up support for additional sizes of OLED displays, support for the SH1107 controller, and SPI-based OLED support.
|
||||||
|
|
||||||
|
Other configurable items are available and can be found on the [OLED Driver page](https://docs.qmk.fm/#/feature_oled_driver).
|
||||||
|
|
||||||
|
## Full changelist :id=full-changelist
|
||||||
|
|
||||||
|
Core:
|
||||||
|
* Refactor `keyevent_t` for 1ms timing resolution ([#15847](https://github.com/qmk/qmk_firmware/pull/15847))
|
||||||
|
* PS/2 PIO Driver for RP2040 ([#17893](https://github.com/qmk/qmk_firmware/pull/17893))
|
||||||
|
* Relocate various modifier defines ([#18638](https://github.com/qmk/qmk_firmware/pull/18638))
|
||||||
|
* Added PMW3320 driver ([#19543](https://github.com/qmk/qmk_firmware/pull/19543))
|
||||||
|
* Keymap introspection for combos. ([#19670](https://github.com/qmk/qmk_firmware/pull/19670))
|
||||||
|
* Add direction to dynamic_macro_record_start_user ([#19689](https://github.com/qmk/qmk_firmware/pull/19689))
|
||||||
|
* Add Repeat Key ("repeat last key") as a core feature. ([#19700](https://github.com/qmk/qmk_firmware/pull/19700))
|
||||||
|
* [Cleanup] Quantum Painter ([#19825](https://github.com/qmk/qmk_firmware/pull/19825))
|
||||||
|
* Improve robustness of AW20216 driver ([#19849](https://github.com/qmk/qmk_firmware/pull/19849))
|
||||||
|
* Make "detected_host_os()" available on the SLAVE side of the split keyboard ([#19854](https://github.com/qmk/qmk_firmware/pull/19854))
|
||||||
|
* Add RP2040 Community Edition alias for splitkb.com's Liatris controller ([#19966](https://github.com/qmk/qmk_firmware/pull/19966))
|
||||||
|
* Remove some use of keymap.h ([#19980](https://github.com/qmk/qmk_firmware/pull/19980))
|
||||||
|
* Merge upstream changes to uf2conv ([#19993](https://github.com/qmk/qmk_firmware/pull/19993))
|
||||||
|
* Remove keymap.h ([#20004](https://github.com/qmk/qmk_firmware/pull/20004))
|
||||||
|
* Remove some use of keymap.h ([#20006](https://github.com/qmk/qmk_firmware/pull/20006))
|
||||||
|
* Quantum Painter QoL enhancements -- auto-poweroff, auto-flush, buffer sizing ([#20013](https://github.com/qmk/qmk_firmware/pull/20013))
|
||||||
|
* Make Pointing Device Auto Layer more configurable ([#20061](https://github.com/qmk/qmk_firmware/pull/20061))
|
||||||
|
* Add last activity functions for pointing device ([#20079](https://github.com/qmk/qmk_firmware/pull/20079))
|
||||||
|
* Caps Word "Invert on shift" option: pressing Shift inverts the shift state. ([#20092](https://github.com/qmk/qmk_firmware/pull/20092))
|
||||||
|
* Remove bootloader logic from `mcu_selection.mk` ([#20150](https://github.com/qmk/qmk_firmware/pull/20150))
|
||||||
|
* Update qmk_cli container references ([#20154](https://github.com/qmk/qmk_firmware/pull/20154))
|
||||||
|
* Clean up APA102 config and add DD mapping ([#20159](https://github.com/qmk/qmk_firmware/pull/20159))
|
||||||
|
* Sync activity timestamps between sides. ([#20192](https://github.com/qmk/qmk_firmware/pull/20192))
|
||||||
|
* Update Doxygen comments for some headers ([#20194](https://github.com/qmk/qmk_firmware/pull/20194))
|
||||||
|
* Make IGNORE_MOD_TAP_INTERRUPT the default behaviour for mod-taps ([#20211](https://github.com/qmk/qmk_firmware/pull/20211))
|
||||||
|
* Add some helpers to tidy up XAP ([#20235](https://github.com/qmk/qmk_firmware/pull/20235))
|
||||||
|
* Tidy up duplication of MIN/MAX fallback implementations ([#20236](https://github.com/qmk/qmk_firmware/pull/20236))
|
||||||
|
* Optionally keep intermediate file listings in order to do comparisons between builds. ([#20237](https://github.com/qmk/qmk_firmware/pull/20237))
|
||||||
|
* Add basic profiler. ([#20238](https://github.com/qmk/qmk_firmware/pull/20238))
|
||||||
|
* WS2812 driver improvements ([#20262](https://github.com/qmk/qmk_firmware/pull/20262))
|
||||||
|
* typing_heatmap: Add macro to configure increase steps ([#20300](https://github.com/qmk/qmk_firmware/pull/20300))
|
||||||
|
* Migrate `rgblight.pin` and `RGB_DI_PIN` to `ws2812.pin` ([#20303](https://github.com/qmk/qmk_firmware/pull/20303))
|
||||||
|
* Delete config_common.h ([#20312](https://github.com/qmk/qmk_firmware/pull/20312))
|
||||||
|
* Allow EEPROM_DRIVER from info.json ([#20313](https://github.com/qmk/qmk_firmware/pull/20313))
|
||||||
|
* rp2040: *_PAL_MODE overridable for this platform too ([#20314](https://github.com/qmk/qmk_firmware/pull/20314))
|
||||||
|
* Add core/fallback encoder behaviour ([#20320](https://github.com/qmk/qmk_firmware/pull/20320))
|
||||||
|
* OLED Driver improvements ([#20331](https://github.com/qmk/qmk_firmware/pull/20331))
|
||||||
|
* [Chore] Remove stray mod tap interrupt defines and per key functions ([#20347](https://github.com/qmk/qmk_firmware/pull/20347))
|
||||||
|
* Add swap hands toggle functions ([#20381](https://github.com/qmk/qmk_firmware/pull/20381))
|
||||||
|
* Prevent Tri-Layer keys from stopping caps word ([#20398](https://github.com/qmk/qmk_firmware/pull/20398))
|
||||||
|
* quantum/action_util.c: Use uint8_t for oneshot_layer_data ([#20423](https://github.com/qmk/qmk_firmware/pull/20423))
|
||||||
|
* Encoder map direction define. ([#20454](https://github.com/qmk/qmk_firmware/pull/20454))
|
||||||
|
* Realign and size check EECONFIG structures ([#20541](https://github.com/qmk/qmk_firmware/pull/20541))
|
||||||
|
* Clean up ISSI drivers, Add IS31FL3736 support ([#20572](https://github.com/qmk/qmk_firmware/pull/20572))
|
||||||
|
* Add a user callback for pre process record ([#20584](https://github.com/qmk/qmk_firmware/pull/20584))
|
||||||
|
* Disable debug on QP's internal task ([#20623](https://github.com/qmk/qmk_firmware/pull/20623))
|
||||||
|
* Add required string header file ([#20638](https://github.com/qmk/qmk_firmware/pull/20638))
|
||||||
|
* Add Develop is31fl3736 multi drivers ([#20642](https://github.com/qmk/qmk_firmware/pull/20642))
|
||||||
|
* Support PS/2 mouse 9-bit output with MOUSE_EXTENDED_REPORT ([#20734](https://github.com/qmk/qmk_firmware/pull/20734))
|
||||||
|
* BIOI G60/Morgan65: use custom Bluetooth driver ([#20897](https://github.com/qmk/qmk_firmware/pull/20897))
|
||||||
|
* Move `pre_process_record_kb()` before `process_combo()` ([#20969](https://github.com/qmk/qmk_firmware/pull/20969))
|
||||||
|
* Implement UF2 device type id extension tag ([#21029](https://github.com/qmk/qmk_firmware/pull/21029))
|
||||||
|
|
||||||
|
CLI:
|
||||||
|
* Add force support to 'qmk git-submodule' ([#19705](https://github.com/qmk/qmk_firmware/pull/19705))
|
||||||
|
* JSON encoder: improve sorting of layout dict keys ([#19974](https://github.com/qmk/qmk_firmware/pull/19974))
|
||||||
|
* Increase verbosity of make command ([#20172](https://github.com/qmk/qmk_firmware/pull/20172))
|
||||||
|
* Append user variables to the end of make command ([#20177](https://github.com/qmk/qmk_firmware/pull/20177))
|
||||||
|
* Strip API specific output from `qmk info` ([#20234](https://github.com/qmk/qmk_firmware/pull/20234))
|
||||||
|
* `qmk find`: usability improvements ([#20440](https://github.com/qmk/qmk_firmware/pull/20440))
|
||||||
|
* `qmk format-json`: Expose full key path and respect `sort_keys` ([#20836](https://github.com/qmk/qmk_firmware/pull/20836))
|
||||||
|
* Update json2c to use dump_lines ([#21013](https://github.com/qmk/qmk_firmware/pull/21013))
|
||||||
|
|
||||||
|
Submodule updates:
|
||||||
|
* Update ChibiOS to latest stable 21.11.x ([#20470](https://github.com/qmk/qmk_firmware/pull/20470))
|
||||||
|
|
||||||
|
Keyboards:
|
||||||
|
* Allow a larger int for the idle timeout for urbanvanilla keymap ([#19738](https://github.com/qmk/qmk_firmware/pull/19738))
|
||||||
|
* Change aidansmithdotdev/fine40 to use Encoder Map ([#19912](https://github.com/qmk/qmk_firmware/pull/19912))
|
||||||
|
* Custom keycodes in JSON ([#19925](https://github.com/qmk/qmk_firmware/pull/19925))
|
||||||
|
* Remove `"w":1` and `"h":1` from info.json ([#19961](https://github.com/qmk/qmk_firmware/pull/19961))
|
||||||
|
* Move matrix config to info.json, part 1 ([#19985](https://github.com/qmk/qmk_firmware/pull/19985))
|
||||||
|
* Move matrix config to info.json, part 2 ([#19987](https://github.com/qmk/qmk_firmware/pull/19987))
|
||||||
|
* Move matrix config to info.json, part 3 ([#19991](https://github.com/qmk/qmk_firmware/pull/19991))
|
||||||
|
* Move matrix config to info.json, part 4 ([#20001](https://github.com/qmk/qmk_firmware/pull/20001))
|
||||||
|
* Move matrix config to info.json, part 5 ([#20003](https://github.com/qmk/qmk_firmware/pull/20003))
|
||||||
|
* Move matrix config to info.json, part 6 ([#20019](https://github.com/qmk/qmk_firmware/pull/20019))
|
||||||
|
* Move matrix config to info.json, part 7 ([#20020](https://github.com/qmk/qmk_firmware/pull/20020))
|
||||||
|
* Move matrix config to info.json, part 8 ([#20030](https://github.com/qmk/qmk_firmware/pull/20030))
|
||||||
|
* Remove empty rules.mk from keymaps ([#20056](https://github.com/qmk/qmk_firmware/pull/20056))
|
||||||
|
* Adjust offset for some layouts ([#20075](https://github.com/qmk/qmk_firmware/pull/20075))
|
||||||
|
* Remove useless "ifdef KEYBOARD_*" ([#20078](https://github.com/qmk/qmk_firmware/pull/20078))
|
||||||
|
* Remove pointless `USE_I2C` blocks in keyboard headers ([#20084](https://github.com/qmk/qmk_firmware/pull/20084))
|
||||||
|
* Add support for ISO version of Durgod Hades ([#20110](https://github.com/qmk/qmk_firmware/pull/20110))
|
||||||
|
* Consolidate Binepad BN009 R1 and R2 into common folder ([#20113](https://github.com/qmk/qmk_firmware/pull/20113))
|
||||||
|
* Remove more empty headers ([#20155](https://github.com/qmk/qmk_firmware/pull/20155))
|
||||||
|
* Remove trailing zeroes in info.json layouts ([#20156](https://github.com/qmk/qmk_firmware/pull/20156))
|
||||||
|
* Clean up usage of `QMK_KEYBOARD_H` ([#20167](https://github.com/qmk/qmk_firmware/pull/20167))
|
||||||
|
* Move Keychron Q0 and Q0 Plus data-driven configuration; `keychron` keymap `rules.mk` cleanup ([#20168](https://github.com/qmk/qmk_firmware/pull/20168))
|
||||||
|
* Move ortho & numpad layouts to data driven ([#20183](https://github.com/qmk/qmk_firmware/pull/20183))
|
||||||
|
* Remove `RGB_DI_PIN` ifdefs ([#20218](https://github.com/qmk/qmk_firmware/pull/20218))
|
||||||
|
* Add the KJ-Modify RS40 PCB keyboard ([#20243](https://github.com/qmk/qmk_firmware/pull/20243))
|
||||||
|
* Move `WS2812_DRIVER` to data driven ([#20248](https://github.com/qmk/qmk_firmware/pull/20248))
|
||||||
|
* [jacky_studio/piggy60] move AVR PCB under rev1 ([#20253](https://github.com/qmk/qmk_firmware/pull/20253))
|
||||||
|
* Move 75% and 96% layouts to data driven ([#20289](https://github.com/qmk/qmk_firmware/pull/20289))
|
||||||
|
* Move split layouts to data driven ([#20290](https://github.com/qmk/qmk_firmware/pull/20290))
|
||||||
|
* Move 66% and 68% layouts to data driven ([#20293](https://github.com/qmk/qmk_firmware/pull/20293))
|
||||||
|
* add jacky_studio/piggy60/rev2 ([#20297](https://github.com/qmk/qmk_firmware/pull/20297))
|
||||||
|
* Move 65% layouts to data driven ([#20308](https://github.com/qmk/qmk_firmware/pull/20308))
|
||||||
|
* Move TKL F13 and FRL layouts to data driven ([#20310](https://github.com/qmk/qmk_firmware/pull/20310))
|
||||||
|
* Remove some use of keymap.h ([#20316](https://github.com/qmk/qmk_firmware/pull/20316))
|
||||||
|
* Move fullsize layouts to data driven ([#20317](https://github.com/qmk/qmk_firmware/pull/20317))
|
||||||
|
* Add 36-key layout for Beekeeb Piantor ([#20328](https://github.com/qmk/qmk_firmware/pull/20328))
|
||||||
|
* Add sriwedari70 and move kamigakushi to new folder ([#20334](https://github.com/qmk/qmk_firmware/pull/20334))
|
||||||
|
* Move TKL layouts to data driven ([#20337](https://github.com/qmk/qmk_firmware/pull/20337))
|
||||||
|
* Move Alice and Ergodox layouts to data driven ([#20340](https://github.com/qmk/qmk_firmware/pull/20340))
|
||||||
|
* Move small macropad-ish layouts to data driven ([#20341](https://github.com/qmk/qmk_firmware/pull/20341))
|
||||||
|
* Move `default` layouts to data driven ([#20349](https://github.com/qmk/qmk_firmware/pull/20349))
|
||||||
|
* Move `RGB_MATRIX_DRIVER` to data driven ([#20350](https://github.com/qmk/qmk_firmware/pull/20350))
|
||||||
|
* Move split space/backspace layouts to data driven ([#20356](https://github.com/qmk/qmk_firmware/pull/20356))
|
||||||
|
* Move single `LAYOUT`s to data driven ([#20365](https://github.com/qmk/qmk_firmware/pull/20365))
|
||||||
|
* Add encoder map for Iris Rev. 5 VIA ([#20412](https://github.com/qmk/qmk_firmware/pull/20412))
|
||||||
|
* Move remaining `LAYOUT`s to data driven ([#20422](https://github.com/qmk/qmk_firmware/pull/20422))
|
||||||
|
* Move single `LAYOUT_all`s to data driven ([#20430](https://github.com/qmk/qmk_firmware/pull/20430))
|
||||||
|
* 4pplet/yakiimo Layout Macro Conversion and Additions ([#20436](https://github.com/qmk/qmk_firmware/pull/20436))
|
||||||
|
* Move single `60_ansi`, `60_hhkb` and `60_iso` layouts to data driven ([#20438](https://github.com/qmk/qmk_firmware/pull/20438))
|
||||||
|
* Update brauner preonic layout ([#20439](https://github.com/qmk/qmk_firmware/pull/20439))
|
||||||
|
* AEBoards Satellite Rev1 Layout Macro Conversion ([#20442](https://github.com/qmk/qmk_firmware/pull/20442))
|
||||||
|
* Acheron Austin Layout Macro Conversion and Additions ([#20443](https://github.com/qmk/qmk_firmware/pull/20443))
|
||||||
|
* Move remaining `LAYOUT_all`s to data driven ([#20463](https://github.com/qmk/qmk_firmware/pull/20463))
|
||||||
|
* Update lotus58 RGB config ([#20468](https://github.com/qmk/qmk_firmware/pull/20468))
|
||||||
|
* Cleanup `ekow/akira` ([#20474](https://github.com/qmk/qmk_firmware/pull/20474))
|
||||||
|
* Move 60% layouts to data driven ([#20477](https://github.com/qmk/qmk_firmware/pull/20477))
|
||||||
|
* Move DZ60 and MJ6XY layouts to data driven ([#20478](https://github.com/qmk/qmk_firmware/pull/20478))
|
||||||
|
* AEBoards Constellation Layout Macro Updates ([#20487](https://github.com/qmk/qmk_firmware/pull/20487))
|
||||||
|
* AI03 Equinox Layout Macro Additions ([#20488](https://github.com/qmk/qmk_firmware/pull/20488))
|
||||||
|
* AI03 Vega Layout Macro Additions ([#20489](https://github.com/qmk/qmk_firmware/pull/20489))
|
||||||
|
* AKB OGR Layout Macro Additions ([#20490](https://github.com/qmk/qmk_firmware/pull/20490))
|
||||||
|
* AKB Vero Layout Macro Additions ([#20491](https://github.com/qmk/qmk_firmware/pull/20491))
|
||||||
|
* Alf DC60 Layout Macro Additions ([#20494](https://github.com/qmk/qmk_firmware/pull/20494))
|
||||||
|
* Alf X2 Layout Macro Additions ([#20495](https://github.com/qmk/qmk_firmware/pull/20495))
|
||||||
|
* Koolertron AMAG23 Touch-Up ([#20496](https://github.com/qmk/qmk_firmware/pull/20496))
|
||||||
|
* BIOI G60 Layout Macro Additions ([#20498](https://github.com/qmk/qmk_firmware/pull/20498))
|
||||||
|
* BIOI Morgan65 Layout Macro Additions ([#20499](https://github.com/qmk/qmk_firmware/pull/20499))
|
||||||
|
* BIOI S65 Layout Macro Additions ([#20500](https://github.com/qmk/qmk_firmware/pull/20500))
|
||||||
|
* Boston Layout Macro Additions ([#20504](https://github.com/qmk/qmk_firmware/pull/20504))
|
||||||
|
* Potato65S Layout Macro Additions ([#20508](https://github.com/qmk/qmk_firmware/pull/20508))
|
||||||
|
* Move miscellaneous layouts to data driven ([#20516](https://github.com/qmk/qmk_firmware/pull/20516))
|
||||||
|
* Cable Car Designs Cypher rev6 Layout Additions and Touch-Up ([#20518](https://github.com/qmk/qmk_firmware/pull/20518))
|
||||||
|
* Caffeinated Studios Serpent65 Layout Macro Additions ([#20519](https://github.com/qmk/qmk_firmware/pull/20519))
|
||||||
|
* CannonKeys Adelie Layout Macro Additions ([#20546](https://github.com/qmk/qmk_firmware/pull/20546))
|
||||||
|
* CannonKeys Aella Layout Macro Additions ([#20547](https://github.com/qmk/qmk_firmware/pull/20547))
|
||||||
|
* CannonKeys Balance Layout Macro Additions and Touch-Up ([#20548](https://github.com/qmk/qmk_firmware/pull/20548))
|
||||||
|
* CannonKeys Brutal v2 1800 Layout Macro Additions ([#20549](https://github.com/qmk/qmk_firmware/pull/20549))
|
||||||
|
* CannonKeys Brutal v2 65 Layout Macro Additions ([#20552](https://github.com/qmk/qmk_firmware/pull/20552))
|
||||||
|
* CannonKeys Cloudline Layout Macro Additions ([#20553](https://github.com/qmk/qmk_firmware/pull/20553))
|
||||||
|
* CannonKeys Crin Layout Macro Additions ([#20554](https://github.com/qmk/qmk_firmware/pull/20554))
|
||||||
|
* CannonKeys DevastatingTKL Layout Macro Additions ([#20555](https://github.com/qmk/qmk_firmware/pull/20555))
|
||||||
|
* CannonKeys Ellipse Layout Macro Additions ([#20558](https://github.com/qmk/qmk_firmware/pull/20558))
|
||||||
|
* CannonKeys Ellipse Hotswap Layout Macro Addition & Touch-Up ([#20560](https://github.com/qmk/qmk_firmware/pull/20560))
|
||||||
|
* CannonKeys Gentoo Layout Macro Additions ([#20561](https://github.com/qmk/qmk_firmware/pull/20561))
|
||||||
|
* CannonKeys Gentoo Hotswap Touch-Up ([#20562](https://github.com/qmk/qmk_firmware/pull/20562))
|
||||||
|
* CannonKeys HoodrowG Layout Macro Additions ([#20563](https://github.com/qmk/qmk_firmware/pull/20563))
|
||||||
|
* CannonKeys Moment Layout Macro Additions ([#20564](https://github.com/qmk/qmk_firmware/pull/20564))
|
||||||
|
* CannonKeys Moment Hotswap Touch-Up ([#20565](https://github.com/qmk/qmk_firmware/pull/20565))
|
||||||
|
* CannonKeys Nearfield Layout Macro Addition ([#20566](https://github.com/qmk/qmk_firmware/pull/20566))
|
||||||
|
* CannonKeys Obliterated75 Layout Macro Additions ([#20567](https://github.com/qmk/qmk_firmware/pull/20567))
|
||||||
|
* CannonKeys Onyx Layout Macro Additions ([#20568](https://github.com/qmk/qmk_firmware/pull/20568))
|
||||||
|
* CannonKeys Rekt1800 Layout Macro Additions ([#20569](https://github.com/qmk/qmk_firmware/pull/20569))
|
||||||
|
* CannonKeys Serenity Layout Macro Additions ([#20570](https://github.com/qmk/qmk_firmware/pull/20570))
|
||||||
|
* CannonKeys Vector Layout Macro Additions ([#20571](https://github.com/qmk/qmk_firmware/pull/20571))
|
||||||
|
* Carbo65 Community Layout support ([#20580](https://github.com/qmk/qmk_firmware/pull/20580))
|
||||||
|
* cest73 TKM Layout Macro Additions ([#20583](https://github.com/qmk/qmk_firmware/pull/20583))
|
||||||
|
* Charue Charon Layout Macro Additions ([#20585](https://github.com/qmk/qmk_firmware/pull/20585))
|
||||||
|
* Charue Sunsetter R2 Layout Macro Additions ([#20586](https://github.com/qmk/qmk_firmware/pull/20586))
|
||||||
|
* Remove `FLIP_HALF` layouts and move to data driven ([#20588](https://github.com/qmk/qmk_firmware/pull/20588))
|
||||||
|
* update ymdk/id75/rules.mk for develop ([#20592](https://github.com/qmk/qmk_firmware/pull/20592))
|
||||||
|
* CherryB Studio CB1800 Layout Macro Additions ([#20593](https://github.com/qmk/qmk_firmware/pull/20593))
|
||||||
|
* CherryB Studio CB65 Layout Macro Additions ([#20594](https://github.com/qmk/qmk_firmware/pull/20594))
|
||||||
|
* CherryB Studio CB87RGB Layout Macro Additions ([#20595](https://github.com/qmk/qmk_firmware/pull/20595))
|
||||||
|
* CheckerBoards G_IDB60 Layout Macro Edits ([#20596](https://github.com/qmk/qmk_firmware/pull/20596))
|
||||||
|
* CherryB Studio CB87v2 Layout Macro Additions ([#20597](https://github.com/qmk/qmk_firmware/pull/20597))
|
||||||
|
* CX60 Community Layout Support ([#20598](https://github.com/qmk/qmk_firmware/pull/20598))
|
||||||
|
* Demiurge Layout Macro Touch-Up ([#20599](https://github.com/qmk/qmk_firmware/pull/20599))
|
||||||
|
* Ducky One 2 SF 1967ST Layout Macro Additions ([#20600](https://github.com/qmk/qmk_firmware/pull/20600))
|
||||||
|
* Move `FORCE_NKRO` to data driven ([#20604](https://github.com/qmk/qmk_firmware/pull/20604))
|
||||||
|
* dyz Synthesis60 Layout Macro Addition ([#20610](https://github.com/qmk/qmk_firmware/pull/20610))
|
||||||
|
* DZTech Bocc Layout Macro Additions ([#20611](https://github.com/qmk/qmk_firmware/pull/20611))
|
||||||
|
* E88 Layout Macro Additions ([#20612](https://github.com/qmk/qmk_firmware/pull/20612))
|
||||||
|
* Emery65 Layout Macro Additions ([#20613](https://github.com/qmk/qmk_firmware/pull/20613))
|
||||||
|
* EvyD13 MX5160 Layout Macro Additions ([#20614](https://github.com/qmk/qmk_firmware/pull/20614))
|
||||||
|
* FJLabs AD65 Layout Macro Additions ([#20619](https://github.com/qmk/qmk_firmware/pull/20619))
|
||||||
|
* FJLabs Avalon Layout Additions and Touch-Up ([#20620](https://github.com/qmk/qmk_firmware/pull/20620))
|
||||||
|
* FJLabs Midway60 Layout Macro Additions ([#20621](https://github.com/qmk/qmk_firmware/pull/20621))
|
||||||
|
* FJLabs Polaris Layout Additions and Touch-Up ([#20622](https://github.com/qmk/qmk_firmware/pull/20622))
|
||||||
|
* FJLabs Sinanju WK Layout Additions and Touch-Up ([#20628](https://github.com/qmk/qmk_firmware/pull/20628))
|
||||||
|
* LFK87 refactor ([#20635](https://github.com/qmk/qmk_firmware/pull/20635))
|
||||||
|
* Fox Lab Time80 Layout Macro Additions ([#20636](https://github.com/qmk/qmk_firmware/pull/20636))
|
||||||
|
* FJLabs Solanis Layout Macro Additions ([#20639](https://github.com/qmk/qmk_firmware/pull/20639))
|
||||||
|
* GrayStudio Aero 75 Refactor and Touch-Up ([#20640](https://github.com/qmk/qmk_firmware/pull/20640))
|
||||||
|
* Move `USB_MAX_POWER_CONSUMPTION` to data driven ([#20648](https://github.com/qmk/qmk_firmware/pull/20648))
|
||||||
|
* `info.json` whitespace cleanups ([#20651](https://github.com/qmk/qmk_firmware/pull/20651))
|
||||||
|
* Hand88 Layout Macro Additions ([#20657](https://github.com/qmk/qmk_firmware/pull/20657))
|
||||||
|
* Cyberstar Handwired Layout Macro Additions ([#20658](https://github.com/qmk/qmk_firmware/pull/20658))
|
||||||
|
* split_65 Handwired Layout Macro Addition and Touch-Up ([#20659](https://github.com/qmk/qmk_firmware/pull/20659))
|
||||||
|
* Bebol Handwired Layout Macro Additions ([#20660](https://github.com/qmk/qmk_firmware/pull/20660))
|
||||||
|
* Glacier Handwired Layout Macro Addition and Touch-Up ([#20661](https://github.com/qmk/qmk_firmware/pull/20661))
|
||||||
|
* Koalafications Handwired Layout Macro Additions ([#20662](https://github.com/qmk/qmk_firmware/pull/20662))
|
||||||
|
* The Galleon Handwired Layout Macro Additions ([#20663](https://github.com/qmk/qmk_firmware/pull/20663))
|
||||||
|
* More `info.json` whitespace cleanups ([#20665](https://github.com/qmk/qmk_firmware/pull/20665))
|
||||||
|
* Remove use of layout macros for LFKeyboards LED config ([#20666](https://github.com/qmk/qmk_firmware/pull/20666))
|
||||||
|
* Helix rev2: remove 4 rows option ([#20667](https://github.com/qmk/qmk_firmware/pull/20667))
|
||||||
|
* Wakizashi40 Handwired Touch-Up ([#20671](https://github.com/qmk/qmk_firmware/pull/20671))
|
||||||
|
* yttyx: convert readme to utf-8 encoding ([#20672](https://github.com/qmk/qmk_firmware/pull/20672))
|
||||||
|
* Alicia Cook Layout Macro Additions ([#20675](https://github.com/qmk/qmk_firmware/pull/20675))
|
||||||
|
* Primus75 Layout Macro Additions ([#20676](https://github.com/qmk/qmk_firmware/pull/20676))
|
||||||
|
* Volcano660 Layout Macro Additions ([#20677](https://github.com/qmk/qmk_firmware/pull/20677))
|
||||||
|
* Iris Keyboards Iris60 Layout Macro Additions ([#20678](https://github.com/qmk/qmk_firmware/pull/20678))
|
||||||
|
* Irene Layout Macro Additions ([#20679](https://github.com/qmk/qmk_firmware/pull/20679))
|
||||||
|
* Iron180 Layout Macro Additions ([#20680](https://github.com/qmk/qmk_firmware/pull/20680))
|
||||||
|
* kinesis/alvicstep: remove kicad project files ([#20681](https://github.com/qmk/qmk_firmware/pull/20681))
|
||||||
|
* Remove more junk files and scripts ([#20682](https://github.com/qmk/qmk_firmware/pull/20682))
|
||||||
|
* JKeys Design Gentleman65 Layout Macro Addition and Touch-Up ([#20684](https://github.com/qmk/qmk_firmware/pull/20684))
|
||||||
|
* JKeys Design Gentleman65 Suited Edition Layout Macro Addition ([#20685](https://github.com/qmk/qmk_firmware/pull/20685))
|
||||||
|
* add additional layouts to `dactyl_manuform` variants ([#20688](https://github.com/qmk/qmk_firmware/pull/20688))
|
||||||
|
* TheDogKeyboard Layout Macro Addition ([#20689](https://github.com/qmk/qmk_firmware/pull/20689))
|
||||||
|
* KBDfans Bella Soldered Layout Macro Additions ([#20691](https://github.com/qmk/qmk_firmware/pull/20691))
|
||||||
|
* KBDfans Bounce75 Hotswap Touch-Up ([#20692](https://github.com/qmk/qmk_firmware/pull/20692))
|
||||||
|
* KBDfans KBD66 Layout Additions and Refactor ([#20693](https://github.com/qmk/qmk_firmware/pull/20693))
|
||||||
|
* KBDfans Odin RGB Touch-Up ([#20694](https://github.com/qmk/qmk_firmware/pull/20694))
|
||||||
|
* KBDfans Odin Soldered Layout Additions and Touch-Up ([#20695](https://github.com/qmk/qmk_firmware/pull/20695))
|
||||||
|
* keebzdotnet FMe Layout Additions ([#20696](https://github.com/qmk/qmk_firmware/pull/20696))
|
||||||
|
* Kegen G-Boy Layout Additions ([#20697](https://github.com/qmk/qmk_firmware/pull/20697))
|
||||||
|
* Escape Unicode characters in info.json ([#20698](https://github.com/qmk/qmk_firmware/pull/20698))
|
||||||
|
* Kiko's Lab Ellora65 Layout Additions ([#20699](https://github.com/qmk/qmk_firmware/pull/20699))
|
||||||
|
* Even more `info.json` whitespace cleanups ([#20703](https://github.com/qmk/qmk_firmware/pull/20703))
|
||||||
|
* kkatano Bakeneko 65 V3 Layout Additions ([#20706](https://github.com/qmk/qmk_firmware/pull/20706))
|
||||||
|
* kopibeng MNK65 Layout Additions ([#20708](https://github.com/qmk/qmk_firmware/pull/20708))
|
||||||
|
* kopibeng Typ65+ Layout Additions ([#20710](https://github.com/qmk/qmk_firmware/pull/20710))
|
||||||
|
* kopibeng XT60 Layout Additions ([#20711](https://github.com/qmk/qmk_firmware/pull/20711))
|
||||||
|
* kopibeng XT60_SINGA Layout Additions ([#20712](https://github.com/qmk/qmk_firmware/pull/20712))
|
||||||
|
* kopibeng XT8x Layout Additions ([#20713](https://github.com/qmk/qmk_firmware/pull/20713))
|
||||||
|
* Lefty Touch-Up ([#20714](https://github.com/qmk/qmk_firmware/pull/20714))
|
||||||
|
* Loki65 Layout Additions ([#20715](https://github.com/qmk/qmk_firmware/pull/20715))
|
||||||
|
* Lucid Alexa Solder Layout Additions ([#20716](https://github.com/qmk/qmk_firmware/pull/20716))
|
||||||
|
* Lucid Phantom Soldered Layout Additions ([#20717](https://github.com/qmk/qmk_firmware/pull/20717))
|
||||||
|
* Leftover30 Layout Addition ([#20718](https://github.com/qmk/qmk_firmware/pull/20718))
|
||||||
|
* Matrix Cain RE Touch-Up ([#20719](https://github.com/qmk/qmk_firmware/pull/20719))
|
||||||
|
* Matrix Lab 8XV1.2 OG Layout Updates ([#20720](https://github.com/qmk/qmk_firmware/pull/20720))
|
||||||
|
* Mechlovin Studio Hex6C Layout Additions ([#20722](https://github.com/qmk/qmk_firmware/pull/20722))
|
||||||
|
* Mechlovin.Studio Rogue87 Rev.1 Layout Additions ([#20724](https://github.com/qmk/qmk_firmware/pull/20724))
|
||||||
|
* Mechlovin.Studio Rouge87 Rev.1 Layout Additions ([#20725](https://github.com/qmk/qmk_firmware/pull/20725))
|
||||||
|
* Mechlovin.Studio infinity87 Rev.1 Layout Additions ([#20726](https://github.com/qmk/qmk_firmware/pull/20726))
|
||||||
|
* Mechlovin.Studio Infinity87 RGB Rev1 Layout Additions ([#20727](https://github.com/qmk/qmk_firmware/pull/20727))
|
||||||
|
* Mechlovin9 Layout Addition ([#20728](https://github.com/qmk/qmk_firmware/pull/20728))
|
||||||
|
* 1upkeyboards/pi50 WS2812_DI_PIN patch for develop ([#20731](https://github.com/qmk/qmk_firmware/pull/20731))
|
||||||
|
* Mechlovin.Studio Infinity87 Rev.2 Layout Additions ([#20735](https://github.com/qmk/qmk_firmware/pull/20735))
|
||||||
|
* Mechlovin.Studio Olly JF Layout Additions ([#20736](https://github.com/qmk/qmk_firmware/pull/20736))
|
||||||
|
* Mechlovin Studio Serratus Layout Additions ([#20737](https://github.com/qmk/qmk_firmware/pull/20737))
|
||||||
|
* MechWild Mercutio Layout Addition ([#20738](https://github.com/qmk/qmk_firmware/pull/20738))
|
||||||
|
* MisterKnife Knife66 ISO Layout Addition ([#20739](https://github.com/qmk/qmk_firmware/pull/20739))
|
||||||
|
* MNK1800s Layout Addition ([#20740](https://github.com/qmk/qmk_firmware/pull/20740))
|
||||||
|
* MNK75 Layout Additions ([#20741](https://github.com/qmk/qmk_firmware/pull/20741))
|
||||||
|
* Mode SixtyFive S Layout Additions ([#20742](https://github.com/qmk/qmk_firmware/pull/20742))
|
||||||
|
* Mode SeventyFive H Layout Addition ([#20743](https://github.com/qmk/qmk_firmware/pull/20743))
|
||||||
|
* Monstargear XO87 Soldered Layout Additions ([#20744](https://github.com/qmk/qmk_firmware/pull/20744))
|
||||||
|
* MTBKeys MTB60 Solder Layout Additions ([#20745](https://github.com/qmk/qmk_firmware/pull/20745))
|
||||||
|
* Nix Keyboards Day Off 60 Touch-Up and Layout Additions ([#20746](https://github.com/qmk/qmk_firmware/pull/20746))
|
||||||
|
* Kastenwagen 1840 Layout Addition ([#20747](https://github.com/qmk/qmk_firmware/pull/20747))
|
||||||
|
* Kastenwagen 48 Layout Addition ([#20748](https://github.com/qmk/qmk_firmware/pull/20748))
|
||||||
|
* NovelKeys NK87 Touch-Up ([#20749](https://github.com/qmk/qmk_firmware/pull/20749))
|
||||||
|
* NovelKeys NK87B Touch-Up ([#20750](https://github.com/qmk/qmk_firmware/pull/20750))
|
||||||
|
* Noxary 378 Layout Addition ([#20751](https://github.com/qmk/qmk_firmware/pull/20751))
|
||||||
|
* Noxary Valhalla Layout Addition ([#20752](https://github.com/qmk/qmk_firmware/pull/20752))
|
||||||
|
* Nightly Boards/DeskDaily Daily60 Layout Additions ([#20753](https://github.com/qmk/qmk_firmware/pull/20753))
|
||||||
|
* Odelia Touch-Up ([#20754](https://github.com/qmk/qmk_firmware/pull/20754))
|
||||||
|
* One Key Co Dango40 Touch-Up and Layout Addition ([#20755](https://github.com/qmk/qmk_firmware/pull/20755))
|
||||||
|
* P3D Glitch Layout Addition ([#20763](https://github.com/qmk/qmk_firmware/pull/20763))
|
||||||
|
* Pearl Boards Pandora Layout Additions ([#20764](https://github.com/qmk/qmk_firmware/pull/20764))
|
||||||
|
* Pearl Boards Pearl Layout Addition ([#20765](https://github.com/qmk/qmk_firmware/pull/20765))
|
||||||
|
* support boards with APM32 instead of the STM32 ([#20770](https://github.com/qmk/qmk_firmware/pull/20770))
|
||||||
|
* Pearl Boards Zeus Layout Additions ([#20773](https://github.com/qmk/qmk_firmware/pull/20773))
|
||||||
|
* Peej Rosaline Staggered Layout Additions ([#20774](https://github.com/qmk/qmk_firmware/pull/20774))
|
||||||
|
* plywrks Lune Layout Touch-Up ([#20775](https://github.com/qmk/qmk_firmware/pull/20775))
|
||||||
|
* Project Keyboard Signature65 Layout Additions ([#20776](https://github.com/qmk/qmk_firmware/pull/20776))
|
||||||
|
* protoTypist Allison Layout Additions ([#20777](https://github.com/qmk/qmk_firmware/pull/20777))
|
||||||
|
* Prototypist J-01 Rev1 Layout Additions ([#20778](https://github.com/qmk/qmk_firmware/pull/20778))
|
||||||
|
* Protozoa Cassini Layout Additions ([#20779](https://github.com/qmk/qmk_firmware/pull/20779))
|
||||||
|
* Protozoa P.01 Layout Additions ([#20781](https://github.com/qmk/qmk_firmware/pull/20781))
|
||||||
|
* QwertleKeys Calice Layout Addition ([#20782](https://github.com/qmk/qmk_firmware/pull/20782))
|
||||||
|
* Ramlord WITF Layout Touch-Up and Addition ([#20783](https://github.com/qmk/qmk_firmware/pull/20783))
|
||||||
|
* Rart45: rename LAYOUT_all to LAYOUT ([#20784](https://github.com/qmk/qmk_firmware/pull/20784))
|
||||||
|
* Rart60 Layout Additions ([#20785](https://github.com/qmk/qmk_firmware/pull/20785))
|
||||||
|
* Rart67 Layout Additions ([#20786](https://github.com/qmk/qmk_firmware/pull/20786))
|
||||||
|
* Rart67M: rename LAYOUT_all to LAYOUT ([#20787](https://github.com/qmk/qmk_firmware/pull/20787))
|
||||||
|
* RART75 Layout Additions ([#20788](https://github.com/qmk/qmk_firmware/pull/20788))
|
||||||
|
* RART75 Hotswap Layout Additions ([#20789](https://github.com/qmk/qmk_firmware/pull/20789))
|
||||||
|
* RART75M: rename LAYOUT_all to LAYOUT ([#20790](https://github.com/qmk/qmk_firmware/pull/20790))
|
||||||
|
* RART80 Hotswap Layout Additions ([#20791](https://github.com/qmk/qmk_firmware/pull/20791))
|
||||||
|
* Rartand Layout Additions ([#20799](https://github.com/qmk/qmk_firmware/pull/20799))
|
||||||
|
* Rartlice: rename LAYOUT_all to LAYOUT ([#20800](https://github.com/qmk/qmk_firmware/pull/20800))
|
||||||
|
* Ratio65 Hotswap: rename LAYOUT_all to LAYOUT_65_ansi_blocker ([#20801](https://github.com/qmk/qmk_firmware/pull/20801))
|
||||||
|
* Ratio65 Solder Layout Additions ([#20802](https://github.com/qmk/qmk_firmware/pull/20802))
|
||||||
|
* Specifying the default board file is redundant ([#20807](https://github.com/qmk/qmk_firmware/pull/20807))
|
||||||
|
* RGBKB Pan Layout Additions ([#20809](https://github.com/qmk/qmk_firmware/pull/20809))
|
||||||
|
* saevus cor Layout Additions ([#20810](https://github.com/qmk/qmk_firmware/pull/20810))
|
||||||
|
* Clean up trailing commas from info.json ([#20812](https://github.com/qmk/qmk_firmware/pull/20812))
|
||||||
|
* Enable LTO on salicylic acid 7skb to reduce size ([#20813](https://github.com/qmk/qmk_firmware/pull/20813))
|
||||||
|
* Reduce compiled size for mt64rgb's via keymap ([#20814](https://github.com/qmk/qmk_firmware/pull/20814))
|
||||||
|
* Reduce compiled size for prototypist oceanographer's via keymap ([#20816](https://github.com/qmk/qmk_firmware/pull/20816))
|
||||||
|
* Sauce Mild Layout Additions ([#20818](https://github.com/qmk/qmk_firmware/pull/20818))
|
||||||
|
* VCL x SawnsProjects VCL65 Layout Additions ([#20819](https://github.com/qmk/qmk_firmware/pull/20819))
|
||||||
|
* senselessclay had60 Layout Additions ([#20820](https://github.com/qmk/qmk_firmware/pull/20820))
|
||||||
|
* Space Holdings Nebula12B ([#20821](https://github.com/qmk/qmk_firmware/pull/20821))
|
||||||
|
* SmithRune Iron180 Layout Additions ([#20822](https://github.com/qmk/qmk_firmware/pull/20822))
|
||||||
|
* Stello65 Beta Layout Additions and Clean-Up ([#20824](https://github.com/qmk/qmk_firmware/pull/20824))
|
||||||
|
* Studio Kestra Nue Layout Additions ([#20825](https://github.com/qmk/qmk_firmware/pull/20825))
|
||||||
|
* Switchplate Peripherals 910 Layout Additions ([#20827](https://github.com/qmk/qmk_firmware/pull/20827))
|
||||||
|
* TKC California Layout Addition and Touch-Up ([#20829](https://github.com/qmk/qmk_firmware/pull/20829))
|
||||||
|
* TKC M0lly Layout Additions ([#20830](https://github.com/qmk/qmk_firmware/pull/20830))
|
||||||
|
* TKC TKL A/B87 Layout Additions ([#20831](https://github.com/qmk/qmk_firmware/pull/20831))
|
||||||
|
* Viendi 8L Layout Additions ([#20832](https://github.com/qmk/qmk_firmware/pull/20832))
|
||||||
|
* Viktus Smolka Layout Additions ([#20833](https://github.com/qmk/qmk_firmware/pull/20833))
|
||||||
|
* Viktus SP111 Layout Additions ([#20834](https://github.com/qmk/qmk_firmware/pull/20834))
|
||||||
|
* Viktus SP_Mini Layout Additions ([#20835](https://github.com/qmk/qmk_firmware/pull/20835))
|
||||||
|
* W1-AT Layout Additions ([#20842](https://github.com/qmk/qmk_firmware/pull/20842))
|
||||||
|
* Weirdo Geminate60 Layout Additions ([#20843](https://github.com/qmk/qmk_firmware/pull/20843))
|
||||||
|
* Cypher rev5 Layout Additions ([#20844](https://github.com/qmk/qmk_firmware/pull/20844))
|
||||||
|
* Prophet Layout Additions ([#20845](https://github.com/qmk/qmk_firmware/pull/20845))
|
||||||
|
* Tidy up encoder_map directions ([#20847](https://github.com/qmk/qmk_firmware/pull/20847))
|
||||||
|
* Rama Works Koyu Community Layout Support ([#20848](https://github.com/qmk/qmk_firmware/pull/20848))
|
||||||
|
* Rama Works M65-B Community Layout Support ([#20850](https://github.com/qmk/qmk_firmware/pull/20850))
|
||||||
|
* Rama Works M65-BX Community Layout Support ([#20851](https://github.com/qmk/qmk_firmware/pull/20851))
|
||||||
|
* Rama Works U80-A Community Layout Support ([#20853](https://github.com/qmk/qmk_firmware/pull/20853))
|
||||||
|
* Wilba Tech WT60-B Community Layout Support ([#20854](https://github.com/qmk/qmk_firmware/pull/20854))
|
||||||
|
* Wilba Tech WT60-BX Layout Additions and Touch-Up ([#20855](https://github.com/qmk/qmk_firmware/pull/20855))
|
||||||
|
* Wilba Tech WT60-C Community Layout Support ([#20858](https://github.com/qmk/qmk_firmware/pull/20858))
|
||||||
|
* Wilba Tech WT60-D Layout Addition and Touch-Up ([#20859](https://github.com/qmk/qmk_firmware/pull/20859))
|
||||||
|
* Wilba Tech WT60-G Community Layout Support ([#20860](https://github.com/qmk/qmk_firmware/pull/20860))
|
||||||
|
* Wilba Tech WT60-G2 Community Layout Support ([#20861](https://github.com/qmk/qmk_firmware/pull/20861))
|
||||||
|
* Wilba Tech WT60-H2: rename LAYOUT_all to LAYOUT_60_ansi_tsangan_split_rshift ([#20864](https://github.com/qmk/qmk_firmware/pull/20864))
|
||||||
|
* Wilba Tech WT60-XT Layout Additions and Touch-Up ([#20865](https://github.com/qmk/qmk_firmware/pull/20865))
|
||||||
|
* Wilba Tech WT65-A Community Layout Support and Touch-Up ([#20866](https://github.com/qmk/qmk_firmware/pull/20866))
|
||||||
|
* Wilba Tech WT65-B Layout Addition and Touch-Up ([#20867](https://github.com/qmk/qmk_firmware/pull/20867))
|
||||||
|
* Wilba Tech WT65-F Community Layout Support and Touch-Up ([#20869](https://github.com/qmk/qmk_firmware/pull/20869))
|
||||||
|
* Wilba Tech WT65-FX Community Layout Support ([#20870](https://github.com/qmk/qmk_firmware/pull/20870))
|
||||||
|
* Wilba Tech WT65-G Layout Additions and Touch-Up ([#20871](https://github.com/qmk/qmk_firmware/pull/20871))
|
||||||
|
* Wilba Tech WT65-G2 Layout Additions and Touch-Up ([#20872](https://github.com/qmk/qmk_firmware/pull/20872))
|
||||||
|
* Wilba Tech WT65-XT: rename LAYOUT_all to LAYOUT_65_xt_ansi_blocker_tsangan ([#20873](https://github.com/qmk/qmk_firmware/pull/20873))
|
||||||
|
* Wilba Tech WT65-XTX Layout Additions and Touch-Up ([#20874](https://github.com/qmk/qmk_firmware/pull/20874))
|
||||||
|
* Wilba Tech WT69-A Layout Addition and Touch-Up ([#20875](https://github.com/qmk/qmk_firmware/pull/20875))
|
||||||
|
* Wilba Tech WT70-JB Layout Addition and Touch-Up ([#20876](https://github.com/qmk/qmk_firmware/pull/20876))
|
||||||
|
* Wilba Tech WT75-A Layout Additions and Touch-Up ([#20877](https://github.com/qmk/qmk_firmware/pull/20877))
|
||||||
|
* Wilba Tech WT75-B Layout Additions and Touch-Up ([#20878](https://github.com/qmk/qmk_firmware/pull/20878))
|
||||||
|
* Wilba Tech WT75-C Layout Additions and Touch-Up ([#20879](https://github.com/qmk/qmk_firmware/pull/20879))
|
||||||
|
* Wilba Tech WT80-G Layout Additions and Touch-Up ([#20880](https://github.com/qmk/qmk_firmware/pull/20880))
|
||||||
|
* WinKeys Mini Winni: rename LAYOUT_all to LAYOUT_ortho_2x4 ([#20881](https://github.com/qmk/qmk_firmware/pull/20881))
|
||||||
|
* Scarlet Bandana Layout Additions ([#20882](https://github.com/qmk/qmk_firmware/pull/20882))
|
||||||
|
* Winkeyless B87 Community Layout Support ([#20884](https://github.com/qmk/qmk_firmware/pull/20884))
|
||||||
|
* Xelus AkiS Layout Additions ([#20885](https://github.com/qmk/qmk_firmware/pull/20885))
|
||||||
|
* Xelus Dharma Layout Additions ([#20886](https://github.com/qmk/qmk_firmware/pull/20886))
|
||||||
|
* Xelus Kangaroo Layout Additions ([#20887](https://github.com/qmk/qmk_firmware/pull/20887))
|
||||||
|
* Xelus La+ Layout Addition ([#20888](https://github.com/qmk/qmk_firmware/pull/20888))
|
||||||
|
* Xelus Pachi Mini 32U4 Community Layout Support ([#20889](https://github.com/qmk/qmk_firmware/pull/20889))
|
||||||
|
* Xelus Pachi rev1 Community Layout Support ([#20891](https://github.com/qmk/qmk_firmware/pull/20891))
|
||||||
|
* Xelus Trinity XT TKL Layout Additions ([#20892](https://github.com/qmk/qmk_firmware/pull/20892))
|
||||||
|
* Xelus Valor FRL TKL Layout Additions ([#20893](https://github.com/qmk/qmk_firmware/pull/20893))
|
||||||
|
* YDKB Chili Community Layout Support ([#20895](https://github.com/qmk/qmk_firmware/pull/20895))
|
||||||
|
* YDKB Grape Layout Additions ([#20899](https://github.com/qmk/qmk_firmware/pull/20899))
|
||||||
|
* YMDK Wings Layout Addition ([#20900](https://github.com/qmk/qmk_firmware/pull/20900))
|
||||||
|
* YMDK Wings Hotswap: rename LAYOUT_all to LAYOUT ([#20901](https://github.com/qmk/qmk_firmware/pull/20901))
|
||||||
|
* YMDK YM68 Community Layout Support ([#20906](https://github.com/qmk/qmk_firmware/pull/20906))
|
||||||
|
* Yugo-M Controller Layout Additions ([#20907](https://github.com/qmk/qmk_firmware/pull/20907))
|
||||||
|
* Zicodia TKLFRLNRLMLAO Layout Addition ([#20908](https://github.com/qmk/qmk_firmware/pull/20908))
|
||||||
|
* ZTBoards After Layout Addition ([#20912](https://github.com/qmk/qmk_firmware/pull/20912))
|
||||||
|
* ZTBoards Noon Layout Addition ([#20913](https://github.com/qmk/qmk_firmware/pull/20913))
|
||||||
|
* SawnsProjects Amber80 Solder Community Layout Support ([#20917](https://github.com/qmk/qmk_firmware/pull/20917))
|
||||||
|
* Pearl Boards Atlas Layout Additions ([#20918](https://github.com/qmk/qmk_firmware/pull/20918))
|
||||||
|
* Xiudi XD004: rename LAYOUT_all to LAYOUT_ortho_1x4 ([#20919](https://github.com/qmk/qmk_firmware/pull/20919))
|
||||||
|
* Wilba Tech WT80-BC Community Layout Support ([#20920](https://github.com/qmk/qmk_firmware/pull/20920))
|
||||||
|
* 4pplet Eagle Viper REP Rev B Community Layout Support ([#20921](https://github.com/qmk/qmk_firmware/pull/20921))
|
||||||
|
* FR4Boards unix60 Layout Additions ([#20926](https://github.com/qmk/qmk_firmware/pull/20926))
|
||||||
|
* MC-76K: rename LAYOUT_all to LAYOUT ([#20927](https://github.com/qmk/qmk_firmware/pull/20927))
|
||||||
|
* Mechlovin Studio Jay60 Community Layout Support ([#20928](https://github.com/qmk/qmk_firmware/pull/20928))
|
||||||
|
* MisterKnife Knife66 Layout Additions ([#20929](https://github.com/qmk/qmk_firmware/pull/20929))
|
||||||
|
* MisterKnife Knife66 ISO Layout Additions II ([#20930](https://github.com/qmk/qmk_firmware/pull/20930))
|
||||||
|
* 4pplet Waffling80 Community Layout Support and Touch-Up ([#20932](https://github.com/qmk/qmk_firmware/pull/20932))
|
||||||
|
* Acheron Elongate Delta: rename LAYOUT_all to LAYOUT ([#20956](https://github.com/qmk/qmk_firmware/pull/20956))
|
||||||
|
* ADPenrose Akemipad Layout Addition ([#20957](https://github.com/qmk/qmk_firmware/pull/20957))
|
||||||
|
* ADPenrose Shisaku: rename LAYOUT_all to LAYOUT ([#20958](https://github.com/qmk/qmk_firmware/pull/20958))
|
||||||
|
* AEBoards Aegis Layout Additions ([#20960](https://github.com/qmk/qmk_firmware/pull/20960))
|
||||||
|
* rart/rart80:via: restore rules.mk after #20334 ([#21002](https://github.com/qmk/qmk_firmware/pull/21002))
|
||||||
|
* Remove HHKB RN42 code ([#21007](https://github.com/qmk/qmk_firmware/pull/21007))
|
||||||
|
* Move `thekey` to Drop vendor folder ([#21032](https://github.com/qmk/qmk_firmware/pull/21032))
|
||||||
|
|
||||||
|
Keyboard fixes:
|
||||||
|
* userspace/community layout fixes ([#19998](https://github.com/qmk/qmk_firmware/pull/19998))
|
||||||
|
* Fix layout macro keys with no matrix position ([#20033](https://github.com/qmk/qmk_firmware/pull/20033))
|
||||||
|
* Restore matrix pins for ep/40 ([#20083](https://github.com/qmk/qmk_firmware/pull/20083))
|
||||||
|
* kbdfans/tiger80: remove duplicate keys in info.json ([#20148](https://github.com/qmk/qmk_firmware/pull/20148))
|
||||||
|
* Fixup z70ultra — replace mis-removed file ([#20157](https://github.com/qmk/qmk_firmware/pull/20157))
|
||||||
|
* Fixup CI build for F103C6 onekey. ([#20188](https://github.com/qmk/qmk_firmware/pull/20188))
|
||||||
|
* Fix layouts containing keys with multiple matrix positions ([#20191](https://github.com/qmk/qmk_firmware/pull/20191))
|
||||||
|
* Fix some more missing `#pragma once`s ([#20241](https://github.com/qmk/qmk_firmware/pull/20241))
|
||||||
|
* Fixup CI build for `nack`. ([#20292](https://github.com/qmk/qmk_firmware/pull/20292))
|
||||||
|
* Fixup Pointing device functions ([#20311](https://github.com/qmk/qmk_firmware/pull/20311))
|
||||||
|
* Fix a handful of CLI errors ([#20321](https://github.com/qmk/qmk_firmware/pull/20321))
|
||||||
|
* Fix API errors ([#20326](https://github.com/qmk/qmk_firmware/pull/20326))
|
||||||
|
* Set up DEFAULT_FOLDER for primekb/meridian ([#20367](https://github.com/qmk/qmk_firmware/pull/20367))
|
||||||
|
* Fix up via keymap builds. ([#20383](https://github.com/qmk/qmk_firmware/pull/20383))
|
||||||
|
* Fix up via keymap builds. ([#20397](https://github.com/qmk/qmk_firmware/pull/20397))
|
||||||
|
* Fix some missing QMK_KEYBOARD_H includes in user keymaps ([#20417](https://github.com/qmk/qmk_firmware/pull/20417))
|
||||||
|
* Update ymdk/id75 config ([#20432](https://github.com/qmk/qmk_firmware/pull/20432))
|
||||||
|
* Fix info.json LTO and format encoder definitions ([#20456](https://github.com/qmk/qmk_firmware/pull/20456))
|
||||||
|
* Fixup dymium65 RGB Pin on develop ([#20473](https://github.com/qmk/qmk_firmware/pull/20473))
|
||||||
|
* Fixup missing include in mxss `via` keymap ([#20475](https://github.com/qmk/qmk_firmware/pull/20475))
|
||||||
|
* Fix nk plus ws2812 config ([#20524](https://github.com/qmk/qmk_firmware/pull/20524))
|
||||||
|
* cannonkeys/ellipse_hs: correct layout macro references ([#20577](https://github.com/qmk/qmk_firmware/pull/20577))
|
||||||
|
* Remove use of layout macros for `music_map` ([#20634](https://github.com/qmk/qmk_firmware/pull/20634))
|
||||||
|
* Vertex/angle65 WS2812 pin fix ([#20653](https://github.com/qmk/qmk_firmware/pull/20653))
|
||||||
|
* Fix ws2812 pin for phantagom boards ([#20670](https://github.com/qmk/qmk_firmware/pull/20670))
|
||||||
|
* Fixup 1upkeyboards/pi50 ([#20733](https://github.com/qmk/qmk_firmware/pull/20733))
|
||||||
|
* Fix `test_json2c_no_json()` ([#20756](https://github.com/qmk/qmk_firmware/pull/20756))
|
||||||
|
* Fix mxss rgblight.c compilation issues ([#20804](https://github.com/qmk/qmk_firmware/pull/20804))
|
||||||
|
* Fixup paladin64 ([#20805](https://github.com/qmk/qmk_firmware/pull/20805))
|
||||||
|
* Fixup dogtag ([#20808](https://github.com/qmk/qmk_firmware/pull/20808))
|
||||||
|
* Fixup zwag75 ([#20923](https://github.com/qmk/qmk_firmware/pull/20923))
|
||||||
|
* Fixup latinpadble ([#20924](https://github.com/qmk/qmk_firmware/pull/20924))
|
||||||
|
* Add missing layout data for a handful of boards ([#20931](https://github.com/qmk/qmk_firmware/pull/20931))
|
||||||
|
* Fixup evo70 ([#20949](https://github.com/qmk/qmk_firmware/pull/20949))
|
||||||
|
* Fixup Crkbd default keymap ([#20962](https://github.com/qmk/qmk_firmware/pull/20962))
|
||||||
|
* Fix key display on Corne OLED ([#21044](https://github.com/qmk/qmk_firmware/pull/21044))
|
||||||
|
|
||||||
|
Others:
|
||||||
|
* Add layer-cycle example ([#19069](https://github.com/qmk/qmk_firmware/pull/19069))
|
||||||
|
* Remove remnants of Vagrant. ([#20000](https://github.com/qmk/qmk_firmware/pull/20000))
|
||||||
|
* Develop cleanup IS31FL3736 docs ([#20633](https://github.com/qmk/qmk_firmware/pull/20633))
|
||||||
|
* Organise config/rules <-> info mappings ([#20723](https://github.com/qmk/qmk_firmware/pull/20723))
|
||||||
|
* Add a change log for PR20584 ([#20998](https://github.com/qmk/qmk_firmware/pull/20998))
|
||||||
|
|
||||||
|
Bugs:
|
||||||
|
* Strip whitespace from CONVERT_TO variables ([#19948](https://github.com/qmk/qmk_firmware/pull/19948))
|
||||||
|
* Check all rows have the correct number of columns when parsing `g_led_config` ([#19954](https://github.com/qmk/qmk_firmware/pull/19954))
|
||||||
|
* Fix OSMs getting stuck ([#20034](https://github.com/qmk/qmk_firmware/pull/20034))
|
||||||
|
* Fix rgblight layers when animations aren't enabled ([#20097](https://github.com/qmk/qmk_firmware/pull/20097))
|
||||||
|
* Fixed split keyboard issue where custom LED indicators could activate incorrect LEDs (#20203) ([#20204](https://github.com/qmk/qmk_firmware/pull/20204))
|
||||||
|
* Reduce _validate complexity ([#20274](https://github.com/qmk/qmk_firmware/pull/20274))
|
||||||
|
* `qmk info`: account for ISO enter when calculating layout X offset ([#20325](https://github.com/qmk/qmk_firmware/pull/20325))
|
||||||
|
* Disable specific warnings to mitigate compilation problems with `KEEP_INTERMEDIATES=yes`. ([#20339](https://github.com/qmk/qmk_firmware/pull/20339))
|
||||||
|
* Fix compilation issue with Swap Hands and Encoder Map ([#20348](https://github.com/qmk/qmk_firmware/pull/20348))
|
||||||
|
* Fix preprocessor condition for SPLIT_HAPTIC_ENABLE ([#20411](https://github.com/qmk/qmk_firmware/pull/20411))
|
||||||
|
* Fix compilation issues with PS/2 driver on F4x1 controllers ([#20433](https://github.com/qmk/qmk_firmware/pull/20433))
|
||||||
|
* Fix capital letters not getting sent with sendstring_swiss_fr.h ([#20515](https://github.com/qmk/qmk_firmware/pull/20515))
|
||||||
|
* Duplicate board files for blok converter ([#20629](https://github.com/qmk/qmk_firmware/pull/20629))
|
||||||
|
* Fix Mod-Tap combo regression ([#20669](https://github.com/qmk/qmk_firmware/pull/20669))
|
||||||
|
* Revert use of legacy wear leveling driver now ChibiOS is fixed ([#20806](https://github.com/qmk/qmk_firmware/pull/20806))
|
||||||
|
* Fix compilation error introduced by #20669 ([#20849](https://github.com/qmk/qmk_firmware/pull/20849))
|
||||||
|
* Fix English word list retrieval in qmk generate-autocorrect-data ([#20915](https://github.com/qmk/qmk_firmware/pull/20915))
|
||||||
|
* Improve keymap folder resolution ([#20981](https://github.com/qmk/qmk_firmware/pull/20981))
|
||||||
|
* Fix issue with Repeat Key-Combo test ([#21005](https://github.com/qmk/qmk_firmware/pull/21005))
|
||||||
|
* `qmk info` - Remove printing of "Keyboard Folder" ([#21033](https://github.com/qmk/qmk_firmware/pull/21033))
|
@ -41,7 +41,6 @@
|
|||||||
* [Keymap Overview](keymap.md)
|
* [Keymap Overview](keymap.md)
|
||||||
* Development Environments
|
* Development Environments
|
||||||
* [Docker Guide](getting_started_docker.md)
|
* [Docker Guide](getting_started_docker.md)
|
||||||
* [Vagrant Guide](getting_started_vagrant.md)
|
|
||||||
* Flashing
|
* Flashing
|
||||||
* [Flashing](flashing.md)
|
* [Flashing](flashing.md)
|
||||||
* [Flashing ATmega32A (ps2avrgb)](flashing_bootloadhid.md)
|
* [Flashing ATmega32A (ps2avrgb)](flashing_bootloadhid.md)
|
||||||
@ -71,6 +70,7 @@
|
|||||||
* [Macros](feature_macros.md)
|
* [Macros](feature_macros.md)
|
||||||
* [Mouse Keys](feature_mouse_keys.md)
|
* [Mouse Keys](feature_mouse_keys.md)
|
||||||
* [Programmable Button](feature_programmable_button.md)
|
* [Programmable Button](feature_programmable_button.md)
|
||||||
|
* [Repeat Key](feature_repeat_key.md)
|
||||||
* [Space Cadet Shift](feature_space_cadet.md)
|
* [Space Cadet Shift](feature_space_cadet.md)
|
||||||
* [US ANSI Shifted Keys](keycodes_us_ansi_shifted.md)
|
* [US ANSI Shifted Keys](keycodes_us_ansi_shifted.md)
|
||||||
|
|
||||||
@ -139,7 +139,7 @@
|
|||||||
* Breaking Changes
|
* Breaking Changes
|
||||||
* [Overview](breaking_changes.md)
|
* [Overview](breaking_changes.md)
|
||||||
* [My Pull Request Was Flagged](breaking_changes_instructions.md)
|
* [My Pull Request Was Flagged](breaking_changes_instructions.md)
|
||||||
* [Most Recent ChangeLog](ChangeLog/20230226.md "QMK v0.20.0 - 2023 Feb 26")
|
* [Most Recent ChangeLog](ChangeLog/20230528.md "QMK v0.21.0 - 2023 May 28")
|
||||||
* [Past Breaking Changes](breaking_changes_history.md)
|
* [Past Breaking Changes](breaking_changes_history.md)
|
||||||
|
|
||||||
* C Development
|
* C Development
|
||||||
|
@ -10,27 +10,25 @@ Practically, this means QMK merges the `develop` branch into the `master` branch
|
|||||||
|
|
||||||
## What has been included in past Breaking Changes?
|
## What has been included in past Breaking Changes?
|
||||||
|
|
||||||
|
* [2023 May 28](ChangeLog/20230528.md)
|
||||||
* [2023 Feb 26](ChangeLog/20230226.md)
|
* [2023 Feb 26](ChangeLog/20230226.md)
|
||||||
* [2022 Nov 26](ChangeLog/20221126.md)
|
* [2022 Nov 26](ChangeLog/20221126.md)
|
||||||
* [2022 Aug 27](ChangeLog/20220827.md)
|
|
||||||
* [2022 May 28](ChangeLog/20220528.md)
|
|
||||||
* [2022 Feb 26](ChangeLog/20220226.md)
|
|
||||||
* [Older Breaking Changes](breaking_changes_history.md)
|
* [Older Breaking Changes](breaking_changes_history.md)
|
||||||
|
|
||||||
## When is the next Breaking Change?
|
## When is the next Breaking Change?
|
||||||
|
|
||||||
The next Breaking Change is scheduled for May 28, 2023.
|
The next Breaking Change is scheduled for August 27, 2023.
|
||||||
|
|
||||||
### Important Dates
|
### Important Dates
|
||||||
|
|
||||||
* 2023 Feb 26 - `develop` is tagged with a new release version. Each push to `master` is subsequently merged to `develop` by GitHub actions.
|
* 2023 May 28 - `develop` is tagged with a new release version. Each push to `master` is subsequently merged to `develop` by GitHub actions.
|
||||||
* 2023 Apr 30 - `develop` closed to new PRs.
|
* 2023 Jul 30 - `develop` closed to new PRs.
|
||||||
* 2023 Apr 30 - Call for testers.
|
* 2023 Jul 30 - Call for testers.
|
||||||
* 2023 May 14 - Last day for merges -- after this point `develop` is locked for testing and accepts only bugfixes
|
* 2023 Aug 13 - Last day for merges -- after this point `develop` is locked for testing and accepts only bugfixes
|
||||||
* 2023 May 21 - `develop` is locked, only critical bugfix PRs merged.
|
* 2023 Aug 20 - `develop` is locked, only critical bugfix PRs merged.
|
||||||
* 2023 May 26 - `master` is locked, no PRs merged.
|
* 2023 Aug 25 - `master` is locked, no PRs merged.
|
||||||
* 2023 May 28 - Merge `develop` to `master`.
|
* 2023 Aug 27 - Merge `develop` to `master`.
|
||||||
* 2023 May 28 - `master` is unlocked. PRs can be merged again.
|
* 2023 Aug 27 - `master` is unlocked. PRs can be merged again.
|
||||||
|
|
||||||
## What changes will be included?
|
## What changes will be included?
|
||||||
|
|
||||||
@ -50,7 +48,7 @@ Criteria for acceptance:
|
|||||||
|
|
||||||
Strongly suggested:
|
Strongly suggested:
|
||||||
|
|
||||||
* The PR has a ChangeLog file describing the changes under `<qmk_firmware>/docs/Changelog/20221126`.
|
* The PR has a ChangeLog file describing the changes under `<qmk_firmware>/docs/Changelog/20230827`.
|
||||||
* This should be in Markdown format, with a name in the format `PR12345.md`, substituting the digits for your PRs ID.
|
* This should be in Markdown format, with a name in the format `PR12345.md`, substituting the digits for your PRs ID.
|
||||||
* One strong recommendation that the ChangeLog document matches the PR description on GitHub, so as to ensure traceability.
|
* One strong recommendation that the ChangeLog document matches the PR description on GitHub, so as to ensure traceability.
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
This page links to all previous changelogs from the QMK Breaking Changes process.
|
This page links to all previous changelogs from the QMK Breaking Changes process.
|
||||||
|
|
||||||
|
* [2023 May 28](ChangeLog/20230528.md) - version 0.21.0
|
||||||
* [2023 Feb 26](ChangeLog/20230226.md) - version 0.20.0
|
* [2023 Feb 26](ChangeLog/20230226.md) - version 0.20.0
|
||||||
* [2022 Nov 26](ChangeLog/20221126.md) - version 0.19.0
|
* [2022 Nov 26](ChangeLog/20221126.md) - version 0.19.0
|
||||||
* [2022 Aug 27](ChangeLog/20220827.md) - version 0.18.0
|
* [2022 Aug 27](ChangeLog/20220827.md) - version 0.18.0
|
||||||
|
@ -23,14 +23,6 @@ If it is determined that your submission is a breaking change, there are a few t
|
|||||||
|
|
||||||
If you are contributing core code, and the only reason it needs to go through breaking changes is that you are updating keymaps to match your change, consider whether you can submit your feature in a way that the old keymaps continue to work. Then submit a separate PR that goes through the breaking changes process to remove the old code.
|
If you are contributing core code, and the only reason it needs to go through breaking changes is that you are updating keymaps to match your change, consider whether you can submit your feature in a way that the old keymaps continue to work. Then submit a separate PR that goes through the breaking changes process to remove the old code.
|
||||||
|
|
||||||
### Contribute a ChangeLog Entry
|
|
||||||
|
|
||||||
We require submissions that go through the Breaking Change process to include a changelog entry. The entry should be a short summary of the changes your pull request makes – [each section here started as a changelog](ChangeLog/20190830.md "n.b. This should link to the 2019 Aug 30 Breaking Changes doc - @noroadsleft").
|
|
||||||
|
|
||||||
Your changelog should be located at `docs/ChangeLog/YYYYMMDD/PR####.md`, where `YYYYMMDD` is the date on which QMK's breaking change branch – usually named `develop` – will be merged into the `master` branch, and `####` is the number of your pull request.
|
|
||||||
|
|
||||||
If your submission requires action on the part of users, your changelog should instruct users what action(s) must be taken, or link to a location that does so.
|
|
||||||
|
|
||||||
### Document Your Changes
|
### Document Your Changes
|
||||||
|
|
||||||
Understanding the purpose for your submission, and possible implications or actions it will require can make the review process more straightforward. A changelog may suffice for this purpose, but more extensive changes may require a level of detail that is ill-suited for a changelog.
|
Understanding the purpose for your submission, and possible implications or actions it will require can make the review process more straightforward. A changelog may suffice for this purpose, but more extensive changes may require a level of detail that is ill-suited for a changelog.
|
||||||
|
@ -20,7 +20,7 @@ qmk compile [-c] <configuratorExport.json>
|
|||||||
qmk compile [-c] [-e <var>=<value>] [-j <num_jobs>] -kb <keyboard_name> -km <keymap_name>
|
qmk compile [-c] [-e <var>=<value>] [-j <num_jobs>] -kb <keyboard_name> -km <keymap_name>
|
||||||
```
|
```
|
||||||
|
|
||||||
**Usage in Keyboard Directory**:
|
**Usage in Keyboard Directory**:
|
||||||
|
|
||||||
Must be in keyboard directory with a default keymap, or in keymap directory for keyboard, or supply one with `--keymap <keymap_name>`
|
Must be in keyboard directory with a default keymap, or in keymap directory for keyboard, or supply one with `--keymap <keymap_name>`
|
||||||
```
|
```
|
||||||
@ -44,7 +44,7 @@ $ qmk compile
|
|||||||
or with optional keymap argument
|
or with optional keymap argument
|
||||||
|
|
||||||
```
|
```
|
||||||
$ cd ~/qmk_firmware/keyboards/clueboard/66/rev4
|
$ cd ~/qmk_firmware/keyboards/clueboard/66/rev4
|
||||||
$ qmk compile -km 66_iso
|
$ qmk compile -km 66_iso
|
||||||
Ψ Compiling keymap with make clueboard/66/rev4:66_iso
|
Ψ Compiling keymap with make clueboard/66/rev4:66_iso
|
||||||
...
|
...
|
||||||
@ -58,7 +58,7 @@ $ qmk compile
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
**Usage in Layout Directory**:
|
**Usage in Layout Directory**:
|
||||||
|
|
||||||
Must be under `qmk_firmware/layouts/`, and in a keymap folder.
|
Must be under `qmk_firmware/layouts/`, and in a keymap folder.
|
||||||
```
|
```
|
||||||
@ -149,6 +149,49 @@ To exit out into the parent shell, simply type `exit`.
|
|||||||
qmk cd
|
qmk cd
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `qmk find`
|
||||||
|
|
||||||
|
This command allows for searching through keyboard/keymap targets, filtering by specific criteria. `info.json` and `rules.mk` files contribute to the search data, as well as keymap configurations, and the results can be filtered using "dotty" syntax matching the overall `info.json` file format.
|
||||||
|
|
||||||
|
For example, one could search for all keyboards using STM32F411:
|
||||||
|
|
||||||
|
```
|
||||||
|
qmk find -f 'processor=STM32F411'
|
||||||
|
```
|
||||||
|
|
||||||
|
...and one can further constrain the list to keyboards using STM32F411 as well as rgb_matrix support:
|
||||||
|
|
||||||
|
```
|
||||||
|
qmk find -f 'processor=STM32F411' -f 'features.rgb_matrix=true'
|
||||||
|
```
|
||||||
|
|
||||||
|
The following filter expressions are also supported:
|
||||||
|
|
||||||
|
- `exists(key)`: Match targets where `key` is present.
|
||||||
|
- `absent(key)`: Match targets where `key` is not present.
|
||||||
|
- `contains(key, value)`: Match targets where `key` contains `value`. Can be used for strings, arrays and object keys.
|
||||||
|
- `length(key, value)`: Match targets where the length of `key` is `value`. Can be used for strings, arrays and objects.
|
||||||
|
|
||||||
|
You can also list arbitrary values for each matched target with `--print`:
|
||||||
|
|
||||||
|
```
|
||||||
|
qmk find -f 'processor=STM32F411' -p 'keyboard_name' -p 'features.rgb_matrix'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Usage**:
|
||||||
|
|
||||||
|
```
|
||||||
|
qmk find [-h] [-km KEYMAP] [-p PRINT] [-f FILTER]
|
||||||
|
|
||||||
|
options:
|
||||||
|
-km KEYMAP, --keymap KEYMAP
|
||||||
|
The keymap name to build. Default is 'default'.
|
||||||
|
-p PRINT, --print PRINT
|
||||||
|
For each matched target, print the value of the supplied info.json key. May be passed multiple times.
|
||||||
|
-f FILTER, --filter FILTER
|
||||||
|
Filter the list of keyboards based on their info.json data. Accepts the formats key=value, function(key), or function(key,value), eg. 'features.rgblight=true'. Valid functions are 'absent', 'contains', 'exists' and 'length'. May be passed multiple times; all filters need to match. Value may include wildcards such as '*' and '?'.
|
||||||
|
```
|
||||||
|
|
||||||
## `qmk console`
|
## `qmk console`
|
||||||
|
|
||||||
This command lets you connect to keyboard consoles to get debugging messages. It only works if your keyboard firmware has been compiled with `CONSOLE_ENABLE=yes`.
|
This command lets you connect to keyboard consoles to get debugging messages. It only works if your keyboard firmware has been compiled with `CONSOLE_ENABLE=yes`.
|
||||||
@ -269,7 +312,8 @@ qmk json2c [-o OUTPUT] filename
|
|||||||
|
|
||||||
## `qmk c2json`
|
## `qmk c2json`
|
||||||
|
|
||||||
Creates a keymap.json from a keymap.c.
|
Creates a keymap.json from a keymap.c.
|
||||||
|
|
||||||
**Note:** Parsing C source files is not easy, therefore this subcommand may not work with your keymap. In some cases not using the C pre-processor helps.
|
**Note:** Parsing C source files is not easy, therefore this subcommand may not work with your keymap. In some cases not using the C pre-processor helps.
|
||||||
|
|
||||||
**Usage**:
|
**Usage**:
|
||||||
@ -442,7 +486,7 @@ $ qmk import-kbfirmware ~/Downloads/gh62.json
|
|||||||
|
|
||||||
## `qmk format-text`
|
## `qmk format-text`
|
||||||
|
|
||||||
This command formats text files to have proper line endings.
|
This command formats text files to have proper line endings.
|
||||||
|
|
||||||
Every text file in the repository needs to have Unix (LF) line ending.
|
Every text file in the repository needs to have Unix (LF) line ending.
|
||||||
If you are working on **Windows**, you must ensure that line endings are corrected in order to get your PRs merged.
|
If you are working on **Windows**, you must ensure that line endings are corrected in order to get your PRs merged.
|
||||||
@ -453,7 +497,7 @@ qmk format-text
|
|||||||
|
|
||||||
## `qmk format-c`
|
## `qmk format-c`
|
||||||
|
|
||||||
This command formats C code using clang-format.
|
This command formats C code using clang-format.
|
||||||
|
|
||||||
Run it with no arguments to format all core code that has been changed. Default checks `origin/master` with `git diff`, branch can be changed using `-b <branch_name>`
|
Run it with no arguments to format all core code that has been changed. Default checks `origin/master` with `git diff`, branch can be changed using `-b <branch_name>`
|
||||||
|
|
||||||
@ -556,7 +600,7 @@ qmk kle2json [-f] <filename>
|
|||||||
**Examples**:
|
**Examples**:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ qmk kle2json kle.txt
|
$ qmk kle2json kle.txt
|
||||||
☒ File info.json already exists, use -f or --force to overwrite.
|
☒ File info.json already exists, use -f or --force to overwrite.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -150,7 +150,7 @@ If you define these options you will enable the associated feature, which may in
|
|||||||
* `#define TAPPING_TERM_PER_KEY`
|
* `#define TAPPING_TERM_PER_KEY`
|
||||||
* enables handling for per key `TAPPING_TERM` settings
|
* enables handling for per key `TAPPING_TERM` settings
|
||||||
* `#define RETRO_TAPPING`
|
* `#define RETRO_TAPPING`
|
||||||
* tap anyway, even after TAPPING_TERM, if there was no other key interruption between press and release
|
* tap anyway, even after `TAPPING_TERM`, if there was no other key interruption between press and release
|
||||||
* See [Retro Tapping](tap_hold.md#retro-tapping) for details
|
* See [Retro Tapping](tap_hold.md#retro-tapping) for details
|
||||||
* `#define RETRO_TAPPING_PER_KEY`
|
* `#define RETRO_TAPPING_PER_KEY`
|
||||||
* enables handling for per key `RETRO_TAPPING` settings
|
* enables handling for per key `RETRO_TAPPING` settings
|
||||||
@ -161,9 +161,6 @@ If you define these options you will enable the associated feature, which may in
|
|||||||
* See [Permissive Hold](tap_hold.md#permissive-hold) for details
|
* See [Permissive Hold](tap_hold.md#permissive-hold) for details
|
||||||
* `#define PERMISSIVE_HOLD_PER_KEY`
|
* `#define PERMISSIVE_HOLD_PER_KEY`
|
||||||
* enabled handling for per key `PERMISSIVE_HOLD` settings
|
* enabled handling for per key `PERMISSIVE_HOLD` settings
|
||||||
* `#define IGNORE_MOD_TAP_INTERRUPT`
|
|
||||||
* makes it possible to do rolling combos (zx) with keys that convert to other keys on hold, by enforcing the `TAPPING_TERM` for both keys.
|
|
||||||
* See [Ignore Mod Tap Interrupt](tap_hold.md#ignore-mod-tap-interrupt) for details
|
|
||||||
* `#define QUICK_TAP_TERM 100`
|
* `#define QUICK_TAP_TERM 100`
|
||||||
* tap-then-hold timing to use a dual role key to repeat keycode
|
* tap-then-hold timing to use a dual role key to repeat keycode
|
||||||
* See [Quick Tap Term](tap_hold.md#quick-tap-term)
|
* See [Quick Tap Term](tap_hold.md#quick-tap-term)
|
||||||
@ -189,8 +186,6 @@ If you define these options you will enable the associated feature, which may in
|
|||||||
* how long before oneshot times out
|
* how long before oneshot times out
|
||||||
* `#define ONESHOT_TAP_TOGGLE 2`
|
* `#define ONESHOT_TAP_TOGGLE 2`
|
||||||
* how many taps before oneshot toggle is triggered
|
* how many taps before oneshot toggle is triggered
|
||||||
* `#define COMBO_COUNT 2`
|
|
||||||
* Set this to the number of combos that you're using in the [Combo](feature_combo.md) feature. Or leave it undefined and programmatically set the count.
|
|
||||||
* `#define COMBO_TERM 200`
|
* `#define COMBO_TERM 200`
|
||||||
* how long for the Combo keys to be detected. Defaults to `TAPPING_TERM` if not defined.
|
* how long for the Combo keys to be detected. Defaults to `TAPPING_TERM` if not defined.
|
||||||
* `#define COMBO_MUST_HOLD_MODS`
|
* `#define COMBO_MUST_HOLD_MODS`
|
||||||
@ -217,7 +212,7 @@ If you define these options you will enable the associated feature, which may in
|
|||||||
|
|
||||||
## RGB Light Configuration
|
## RGB Light Configuration
|
||||||
|
|
||||||
* `#define RGB_DI_PIN D7`
|
* `#define WS2812_DI_PIN D7`
|
||||||
* pin the DI on the WS2812 is hooked-up to
|
* pin the DI on the WS2812 is hooked-up to
|
||||||
* `#define RGBLIGHT_LAYERS`
|
* `#define RGBLIGHT_LAYERS`
|
||||||
* Lets you define [lighting layers](feature_rgblight.md?id=lighting-layers) that can be toggled on or off. Great for showing the current keyboard layer or caps lock state.
|
* Lets you define [lighting layers](feature_rgblight.md?id=lighting-layers) that can be toggled on or off. Great for showing the current keyboard layer or caps lock state.
|
||||||
@ -233,7 +228,7 @@ If you define these options you will enable the associated feature, which may in
|
|||||||
* `#define RGBLIGHT_SPLIT`
|
* `#define RGBLIGHT_SPLIT`
|
||||||
* Needed if both halves of the board have RGB LEDs wired directly to the RGB output pin on the controllers instead of passing the output of the left half to the input of the right half
|
* Needed if both halves of the board have RGB LEDs wired directly to the RGB output pin on the controllers instead of passing the output of the left half to the input of the right half
|
||||||
* `#define RGBLED_SPLIT { 6, 6 }`
|
* `#define RGBLED_SPLIT { 6, 6 }`
|
||||||
* number of LEDs connected that are directly wired to `RGB_DI_PIN` on each half of a split keyboard
|
* number of LEDs connected that are directly wired to the RGB pin on each half of a split keyboard
|
||||||
* First value indicates number of LEDs for left half, second value is for the right half
|
* First value indicates number of LEDs for left half, second value is for the right half
|
||||||
* When RGBLED_SPLIT is defined, RGBLIGHT_SPLIT is implicitly defined.
|
* When RGBLED_SPLIT is defined, RGBLIGHT_SPLIT is implicitly defined.
|
||||||
* `#define RGBLIGHT_HUE_STEP 12`
|
* `#define RGBLIGHT_HUE_STEP 12`
|
||||||
|
@ -202,6 +202,62 @@ This function gets called at the end of all QMK processing, before starting the
|
|||||||
|
|
||||||
Similar to `matrix_scan_*`, these are called as often as the MCU can handle. To keep your board responsive, it's suggested to do as little as possible during these function calls, potentially throtting their behaviour if you do indeed require implementing something special.
|
Similar to `matrix_scan_*`, these are called as often as the MCU can handle. To keep your board responsive, it's suggested to do as little as possible during these function calls, potentially throtting their behaviour if you do indeed require implementing something special.
|
||||||
|
|
||||||
|
### Example `void housekeeping_task_user(void)` implementation
|
||||||
|
|
||||||
|
This example will show you how to use `void housekeeping_task_user(void)` to turn off [RGB Light](feature_rgblight.md). For RGB Matrix, the [builtin](https://docs.qmk.fm/#/feature_rgb_matrix?id=additional-configh-options) `RGB_MATRIX_TIMEOUT` should be used.
|
||||||
|
|
||||||
|
First, add the following lines to your keymap's `config.h`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define RGBLIGHT_SLEEP // enable rgblight_suspend() and rgblight_wakeup() in keymap.c
|
||||||
|
#define RGBLIGHT_TIMEOUT 900000 // ms to wait until rgblight time out, 900K ms is 15min.
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, add the following code to your `keymap.c`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
static uint32_t key_timer; // timer for last keyboard activity, use 32bit value and function to make longer idle time possible
|
||||||
|
static void refresh_rgb(void); // refreshes the activity timer and RGB, invoke whenever any activity happens
|
||||||
|
static void check_rgb_timeout(void); // checks if enough time has passed for RGB to timeout
|
||||||
|
bool is_rgb_timeout = false; // store if RGB has timed out or not in a boolean
|
||||||
|
|
||||||
|
void refresh_rgb(void) {
|
||||||
|
key_timer = timer_read32(); // store time of last refresh
|
||||||
|
if (is_rgb_timeout)
|
||||||
|
{
|
||||||
|
is_rgb_timeout = false;
|
||||||
|
rgblight_wakeup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void check_rgb_timeout(void) {
|
||||||
|
if (!is_rgb_timeout && timer_elapsed32(key_timer) > RGBLIGHT_TIMEOUT) // check if RGB has already timeout and if enough time has passed
|
||||||
|
{
|
||||||
|
rgblight_suspend();
|
||||||
|
is_rgb_timeout = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Then, call the above functions from QMK's built in post processing functions like so */
|
||||||
|
/* Runs at the end of each scan loop, check if RGB timeout has occured or not */
|
||||||
|
void housekeeping_task_user(void) {
|
||||||
|
#ifdef RGBLIGHT_TIMEOUT
|
||||||
|
check_rgb_timeout();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
/* Runs after each key press, check if activity occurred */
|
||||||
|
void post_process_record_user(uint16_t keycode, keyrecord_t *record) {
|
||||||
|
#ifdef RGBLIGHT_TIMEOUT
|
||||||
|
if (record->event.pressed)
|
||||||
|
refresh_rgb();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
/* Runs after each encoder tick, check if activity occurred */
|
||||||
|
void post_encoder_update_user(uint8_t index, bool clockwise) {
|
||||||
|
#ifdef RGBLIGHT_TIMEOUT
|
||||||
|
refresh_rgb();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
# Keyboard Idling/Wake Code
|
# Keyboard Idling/Wake Code
|
||||||
|
|
||||||
If the board supports it, it can be "idled", by stopping a number of functions. A good example of this is RGB lights or backlights. This can save on power consumption, or may be better behavior for your keyboard.
|
If the board supports it, it can be "idled", by stopping a number of functions. A good example of this is RGB lights or backlights. This can save on power consumption, or may be better behavior for your keyboard.
|
||||||
@ -209,7 +265,7 @@ If the board supports it, it can be "idled", by stopping a number of functions.
|
|||||||
This is controlled by two functions: `suspend_power_down_*` and `suspend_wakeup_init_*`, which are called when the system board is idled and when it wakes up, respectively.
|
This is controlled by two functions: `suspend_power_down_*` and `suspend_wakeup_init_*`, which are called when the system board is idled and when it wakes up, respectively.
|
||||||
|
|
||||||
|
|
||||||
### Example suspend_power_down_user() and suspend_wakeup_init_user() Implementation
|
### Example `suspend_power_down_user()` and `suspend_wakeup_init_user()` Implementation
|
||||||
|
|
||||||
|
|
||||||
```c
|
```c
|
||||||
|
@ -37,9 +37,9 @@ For more information on bitwise operators in C, click [here](https://en.wikipedi
|
|||||||
|
|
||||||
In practice, this means that you can check whether a given modifier is active with `get_mods() & MOD_BIT(KC_<modifier>)` (see the [list of modifier keycodes](keycodes_basic.md#modifiers)) or with `get_mods() & MOD_MASK_<modifier>` if the difference between left and right hand modifiers is not important and you want to match both. Same thing can be done for one-shot modifiers if you replace `get_mods()` with `get_oneshot_mods()`.
|
In practice, this means that you can check whether a given modifier is active with `get_mods() & MOD_BIT(KC_<modifier>)` (see the [list of modifier keycodes](keycodes_basic.md#modifiers)) or with `get_mods() & MOD_MASK_<modifier>` if the difference between left and right hand modifiers is not important and you want to match both. Same thing can be done for one-shot modifiers if you replace `get_mods()` with `get_oneshot_mods()`.
|
||||||
|
|
||||||
To check that *only* a specific set of mods is active at a time, AND the modifier state and your desired mod mask as explained above and compare the result to the mod mask itself: `get_mods() & <mod mask> == <mod mask>`.
|
To check that *only* a specific set of mods is active at a time, use a simple equality operator: `get_mods() == <mod mask>`.
|
||||||
|
|
||||||
For example, let's say you want to trigger a piece of custom code if one-shot left control and one-shot left shift are on but every other one-shot mods are off. To do so, you can compose the desired mod mask by combining the mod bits for left control and shift with `(MOD_BIT(KC_LCTL) | MOD_BIT(KC_LSFT))` and then plug it in: `get_oneshot_mods() & (MOD_BIT(KC_LCTL) | MOD_BIT(KC_LSFT)) == (MOD_BIT(KC_LCTL) | MOD_BIT(KC_LSFT))`. Using `MOD_MASK_CS` instead for the mod bitmask would have forced you to press four modifier keys (both versions of control and shift) to fulfill the condition.
|
For example, let's say you want to trigger a piece of custom code if one-shot left control and one-shot left shift are on but every other one-shot mods are off. To do so, you can compose the desired mod mask by combining the mod bits for left control and shift with `(MOD_BIT(KC_LCTL) | MOD_BIT(KC_LSFT))` and then plug it in: `get_oneshot_mods() == (MOD_BIT(KC_LCTL) | MOD_BIT(KC_LSFT))`. Using `MOD_MASK_CS` instead for the mod bitmask would have forced you to press four modifier keys (both versions of control and shift) to fulfill the condition.
|
||||||
|
|
||||||
The full list of mod masks is as follows:
|
The full list of mod masks is as follows:
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
|
|||||||
|
|
||||||
case KC_ESC:
|
case KC_ESC:
|
||||||
// Detect the activation of only Left Alt
|
// Detect the activation of only Left Alt
|
||||||
if ((get_mods() & MOD_BIT(KC_LALT)) == MOD_BIT(KC_LALT)) {
|
if (get_mods() == MOD_BIT(KC_LALT)) {
|
||||||
if (record->event.pressed) {
|
if (record->event.pressed) {
|
||||||
// No need to register KC_LALT because it's already active.
|
// No need to register KC_LALT because it's already active.
|
||||||
// The Alt modifier will apply on this KC_TAB.
|
// The Alt modifier will apply on this KC_TAB.
|
||||||
@ -160,6 +160,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
Alternatively, this can be done with [Key Overrides](feature_key_overrides?id=simple-example).
|
||||||
|
|
||||||
# Advanced topics :id=advanced-topics
|
# Advanced topics :id=advanced-topics
|
||||||
|
|
||||||
@ -180,3 +181,7 @@ This page used to encompass a large set of features. We have moved many sections
|
|||||||
## Tap-Hold Configuration Options :id=tap-hold-configuration-options
|
## Tap-Hold Configuration Options :id=tap-hold-configuration-options
|
||||||
|
|
||||||
* [Tap-Hold Configuration Options](tap_hold.md)
|
* [Tap-Hold Configuration Options](tap_hold.md)
|
||||||
|
|
||||||
|
## Key Overrides :id=key-overrides
|
||||||
|
|
||||||
|
* [Key Overrides](feature_key_overrides.md)
|
||||||
|
@ -236,6 +236,18 @@ bool apply_autocorrect(uint8_t backspaces, const char *str) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Autocorrect Status
|
||||||
|
|
||||||
|
Additional user callback functions to manipulate Autocorrect:
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------------------------|----------------------------------------------|
|
||||||
|
| `autocorrect_enable()` | Turns Autocorrect on. |
|
||||||
|
| `autocorrect_disable()` | Turns Autocorrect off. |
|
||||||
|
| `autocorrect_toggle()` | Toggles Autocorrect. |
|
||||||
|
| `autocorrect_is_enabled()` | Returns true if Autocorrect is currently on. |
|
||||||
|
|
||||||
|
|
||||||
## Appendix: Trie binary data format :id=appendix
|
## Appendix: Trie binary data format :id=appendix
|
||||||
|
|
||||||
This section details how the trie is serialized to byte data in autocorrect_data. You don’t need to care about this to use this autocorrection implementation. But it is documented for the record in case anyone is interested in modifying the implementation, or just curious how it works.
|
This section details how the trie is serialized to byte data in autocorrect_data. You don’t need to care about this to use this autocorrection implementation. But it is documented for the record in case anyone is interested in modifying the implementation, or just curious how it works.
|
||||||
|
@ -90,6 +90,26 @@ by defining `IS_COMMAND()` in config.h:
|
|||||||
|
|
||||||
## Customizing Caps Word :id=customizing-caps-word
|
## Customizing Caps Word :id=customizing-caps-word
|
||||||
|
|
||||||
|
### Invert on shift :id=invert-on-shift
|
||||||
|
|
||||||
|
By default, Caps Word turns off when Shift keys are pressed, considering them as
|
||||||
|
word-breaking. Alternatively with the `CAPS_WORD_INVERT_ON_SHIFT` option,
|
||||||
|
pressing the Shift key continues Caps Word and inverts the shift state. This
|
||||||
|
is convenient for uncapitalizing one or a few letters within a word, for
|
||||||
|
example with Caps Word on, typing "D, B, Shift+A, Shift+A, S" produces "DBaaS",
|
||||||
|
or typing "P, D, F, Shift+S" produces "PDFs".
|
||||||
|
|
||||||
|
Enable it by adding in config.h
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define CAPS_WORD_INVERT_ON_SHIFT
|
||||||
|
```
|
||||||
|
|
||||||
|
This option works with regular Shift keys `KC_LSFT` and `KC_RSFT`, mod-tap Shift
|
||||||
|
keys, and one-shot Shift keys. Note that while Caps Word is on, one-shot Shift
|
||||||
|
keys behave like regular Shift keys, and have effect only while they are held.
|
||||||
|
|
||||||
|
|
||||||
### Idle timeout :id=idle-timeout
|
### Idle timeout :id=idle-timeout
|
||||||
|
|
||||||
Caps Word turns off automatically if no keys are pressed for
|
Caps Word turns off automatically if no keys are pressed for
|
||||||
|
@ -4,15 +4,12 @@ The Combo feature is a chording type solution for adding custom actions. It lets
|
|||||||
|
|
||||||
To enable this feature, you need to add `COMBO_ENABLE = yes` to your `rules.mk`.
|
To enable this feature, you need to add `COMBO_ENABLE = yes` to your `rules.mk`.
|
||||||
|
|
||||||
Additionally, in your `config.h`, you'll need to specify the number of combos that you'll be using, by adding `#define COMBO_COUNT 1` (replacing 1 with the number that you're using). It is also possible to not define this and instead set the variable `COMBO_LEN` yourself. There's a trick where we don't need to think about this variable at all. More on this later.
|
|
||||||
|
|
||||||
|
|
||||||
Then, in your `keymap.c` file, you'll need to define a sequence of keys, terminated with `COMBO_END`, and a structure to list the combination of keys, and its resulting action.
|
Then, in your `keymap.c` file, you'll need to define a sequence of keys, terminated with `COMBO_END`, and a structure to list the combination of keys, and its resulting action.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
const uint16_t PROGMEM test_combo1[] = {KC_A, KC_B, COMBO_END};
|
const uint16_t PROGMEM test_combo1[] = {KC_A, KC_B, COMBO_END};
|
||||||
const uint16_t PROGMEM test_combo2[] = {KC_C, KC_D, COMBO_END};
|
const uint16_t PROGMEM test_combo2[] = {KC_C, KC_D, COMBO_END};
|
||||||
combo_t key_combos[COMBO_COUNT] = {
|
combo_t key_combos[] = {
|
||||||
COMBO(test_combo1, KC_ESC),
|
COMBO(test_combo1, KC_ESC),
|
||||||
COMBO(test_combo2, LCTL(KC_Z)), // keycodes with modifiers are possible too!
|
COMBO(test_combo2, LCTL(KC_Z)), // keycodes with modifiers are possible too!
|
||||||
};
|
};
|
||||||
@ -33,7 +30,7 @@ It is possible to overlap combos. Before, with the example below both combos wou
|
|||||||
```c
|
```c
|
||||||
const uint16_t PROGMEM test_combo1[] = {LSFT_T(KC_A), LT(1, KC_B), COMBO_END};
|
const uint16_t PROGMEM test_combo1[] = {LSFT_T(KC_A), LT(1, KC_B), COMBO_END};
|
||||||
const uint16_t PROGMEM test_combo2[] = {LSFT_T(KC_A), LT(1, KC_B), KC_C, COMBO_END};
|
const uint16_t PROGMEM test_combo2[] = {LSFT_T(KC_A), LT(1, KC_B), KC_C, COMBO_END};
|
||||||
combo_t key_combos[COMBO_COUNT] = {
|
combo_t key_combos[] = {
|
||||||
COMBO(test_combo1, KC_ESC)
|
COMBO(test_combo1, KC_ESC)
|
||||||
COMBO(test_combo2, KC_TAB)
|
COMBO(test_combo2, KC_TAB)
|
||||||
};
|
};
|
||||||
@ -41,24 +38,22 @@ combo_t key_combos[COMBO_COUNT] = {
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
A long list of combos can be defined in an `enum` list that ends with `COMBO_LENGTH` and you can leave `COMBO_COUNT` undefined:
|
A long list of combos can be defined in an `enum` list:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
enum combos {
|
enum combos {
|
||||||
AB_ESC,
|
AB_ESC,
|
||||||
JK_TAB,
|
JK_TAB,
|
||||||
QW_SFT,
|
QW_SFT,
|
||||||
SD_LAYER,
|
SD_LAYER
|
||||||
COMBO_LENGTH
|
|
||||||
};
|
};
|
||||||
uint16_t COMBO_LEN = COMBO_LENGTH; // remove the COMBO_COUNT define and use this instead!
|
|
||||||
|
|
||||||
const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END};
|
const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END};
|
||||||
const uint16_t PROGMEM jk_combo[] = {KC_J, KC_K, COMBO_END};
|
const uint16_t PROGMEM jk_combo[] = {KC_J, KC_K, COMBO_END};
|
||||||
const uint16_t PROGMEM qw_combo[] = {KC_Q, KC_W, COMBO_END};
|
const uint16_t PROGMEM qw_combo[] = {KC_Q, KC_W, COMBO_END};
|
||||||
const uint16_t PROGMEM sd_combo[] = {KC_S, KC_D, COMBO_END};
|
const uint16_t PROGMEM sd_combo[] = {KC_S, KC_D, COMBO_END};
|
||||||
|
|
||||||
combo_t key_combos[COMBO_COUNT] = {
|
combo_t key_combos[] = {
|
||||||
[AB_ESC] = COMBO(ab_combo, KC_ESC),
|
[AB_ESC] = COMBO(ab_combo, KC_ESC),
|
||||||
[JK_TAB] = COMBO(jk_combo, KC_TAB),
|
[JK_TAB] = COMBO(jk_combo, KC_TAB),
|
||||||
[QW_SFT] = COMBO(qw_combo, KC_LSFT),
|
[QW_SFT] = COMBO(qw_combo, KC_LSFT),
|
||||||
@ -72,9 +67,7 @@ For a more complicated implementation, you can use the `process_combo_event` fun
|
|||||||
enum combo_events {
|
enum combo_events {
|
||||||
EM_EMAIL,
|
EM_EMAIL,
|
||||||
BSPC_LSFT_CLEAR,
|
BSPC_LSFT_CLEAR,
|
||||||
COMBO_LENGTH
|
|
||||||
};
|
};
|
||||||
uint16_t COMBO_LEN = COMBO_LENGTH; // remove the COMBO_COUNT define and use this instead!
|
|
||||||
|
|
||||||
const uint16_t PROGMEM email_combo[] = {KC_E, KC_M, COMBO_END};
|
const uint16_t PROGMEM email_combo[] = {KC_E, KC_M, COMBO_END};
|
||||||
const uint16_t PROGMEM clear_line_combo[] = {KC_BSPC, KC_LSFT, COMBO_END};
|
const uint16_t PROGMEM clear_line_combo[] = {KC_BSPC, KC_LSFT, COMBO_END};
|
||||||
@ -259,18 +252,6 @@ bool combo_should_trigger(uint16_t combo_index, combo_t *combo, uint16_t keycode
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Variable Length Combos
|
|
||||||
If you leave `COMBO_COUNT` undefined in `config.h`, it allows you to programmatically declare the size of the Combo data structure and avoid updating `COMBO_COUNT`. Instead a variable called `COMBO_LEN` has to be set. It can be set with something similar to the following in `keymap.c`: `uint16_t COMBO_LEN = ARRAY_SIZE(key_combos);` or by adding `COMBO_LENGTH` as the *last* entry in the combo enum and then `uint16_t COMBO_LEN = COMBO_LENGTH;` as such:
|
|
||||||
```c
|
|
||||||
enum myCombos {
|
|
||||||
...,
|
|
||||||
COMBO_LENGTH
|
|
||||||
};
|
|
||||||
uint16_t COMBO_LEN = COMBO_LENGTH;
|
|
||||||
```
|
|
||||||
Regardless of the method used to declare `COMBO_LEN`, this also requires to convert the `combo_t key_combos[COMBO_COUNT] = {...};` line to `combo_t key_combos[] = {...};`.
|
|
||||||
|
|
||||||
|
|
||||||
### Combo timer
|
### Combo timer
|
||||||
|
|
||||||
Normally, the timer is started on the first key press and then reset on every subsequent key press within the `COMBO_TERM`.
|
Normally, the timer is started on the first key press and then reset on every subsequent key press within the `COMBO_TERM`.
|
||||||
@ -300,10 +281,8 @@ Here's an example where a combo resolves to two modifiers, and on key releases t
|
|||||||
|
|
||||||
```c
|
```c
|
||||||
enum combos {
|
enum combos {
|
||||||
AB_MODS,
|
AB_MODS
|
||||||
COMBO_LENGTH
|
|
||||||
};
|
};
|
||||||
uint16_t COMBO_LEN = COMBO_LENGTH;
|
|
||||||
|
|
||||||
const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END};
|
const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END};
|
||||||
|
|
||||||
@ -415,6 +394,4 @@ SUBS(TH_THE, "the", KC_T, KC_H) // SUBS uses SEND_STRING to output the give
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
Now, you can update only one place to add or alter combos. You don't even need to remember to update the `COMBO_COUNT` or the `COMBO_LEN` variables at all. Everything is taken care of. Magic!
|
|
||||||
|
|
||||||
For small to huge ready made dictionaries of combos, you can check out http://combos.gboards.ca/.
|
For small to huge ready made dictionaries of combos, you can check out http://combos.gboards.ca/.
|
||||||
|
@ -17,15 +17,16 @@ Currently the following converters are available:
|
|||||||
| `promicro` | `bit_c_pro` |
|
| `promicro` | `bit_c_pro` |
|
||||||
| `promicro` | `stemcell` |
|
| `promicro` | `stemcell` |
|
||||||
| `promicro` | `bonsai_c4` |
|
| `promicro` | `bonsai_c4` |
|
||||||
| `promicro` | `elite_pi` |
|
|
||||||
| `promicro` | `rp2040_ce` |
|
| `promicro` | `rp2040_ce` |
|
||||||
| `promicro` | `elite_pi` |
|
| `promicro` | `elite_pi` |
|
||||||
| `promicro` | `helios` |
|
| `promicro` | `helios` |
|
||||||
|
| `promicro` | `liatris` |
|
||||||
| `promicro` | `michi` |
|
| `promicro` | `michi` |
|
||||||
| `elite_c` | `stemcell` |
|
| `elite_c` | `stemcell` |
|
||||||
| `elite_c` | `rp2040_ce` |
|
| `elite_c` | `rp2040_ce` |
|
||||||
| `elite_c` | `elite_pi` |
|
| `elite_c` | `elite_pi` |
|
||||||
| `elite_c` | `helios` |
|
| `elite_c` | `helios` |
|
||||||
|
| `elite_c` | `liatris` |
|
||||||
|
|
||||||
See below for more in depth information on each converter.
|
See below for more in depth information on each converter.
|
||||||
|
|
||||||
@ -88,6 +89,7 @@ If a board currently supported in QMK uses a [Pro Micro](https://www.sparkfun.co
|
|||||||
| [customMK Bonsai C4](https://shop.custommk.com/products/bonsai-c4-microcontroller-board) | `bonsai_c4` |
|
| [customMK Bonsai C4](https://shop.custommk.com/products/bonsai-c4-microcontroller-board) | `bonsai_c4` |
|
||||||
| [Elite-Pi](https://keeb.io/products/elite-pi-usb-c-pro-micro-replacement-rp2040) | `elite_pi` |
|
| [Elite-Pi](https://keeb.io/products/elite-pi-usb-c-pro-micro-replacement-rp2040) | `elite_pi` |
|
||||||
| [0xCB Helios](https://keeb.supply/products/0xcb-helios) | `helios` |
|
| [0xCB Helios](https://keeb.supply/products/0xcb-helios) | `helios` |
|
||||||
|
| [Liatris](https://splitkb.com/products/liatris) | `liatris` |
|
||||||
| [Michi](https://github.com/ci-bus/michi-promicro-rp2040) | `michi` |
|
| [Michi](https://github.com/ci-bus/michi-promicro-rp2040) | `michi` |
|
||||||
|
|
||||||
Converter summary:
|
Converter summary:
|
||||||
@ -104,6 +106,7 @@ Converter summary:
|
|||||||
| `rp2040_ce` | `-e CONVERT_TO=rp2040_ce` | `CONVERT_TO=rp2040_ce` | `#ifdef CONVERT_TO_RP2040_CE` |
|
| `rp2040_ce` | `-e CONVERT_TO=rp2040_ce` | `CONVERT_TO=rp2040_ce` | `#ifdef CONVERT_TO_RP2040_CE` |
|
||||||
| `elite_pi` | `-e CONVERT_TO=elite_pi` | `CONVERT_TO=elite_pi` | `#ifdef CONVERT_TO_ELITE_PI` |
|
| `elite_pi` | `-e CONVERT_TO=elite_pi` | `CONVERT_TO=elite_pi` | `#ifdef CONVERT_TO_ELITE_PI` |
|
||||||
| `helios` | `-e CONVERT_TO=helios` | `CONVERT_TO=helios` | `#ifdef CONVERT_TO_HELIOS` |
|
| `helios` | `-e CONVERT_TO=helios` | `CONVERT_TO=helios` | `#ifdef CONVERT_TO_HELIOS` |
|
||||||
|
| `liatris` | `-e CONVERT_TO=liatris` | `CONVERT_TO=liatris` | `#ifdef CONVERT_TO_LIATRIS` |
|
||||||
| `michi` | `-e CONVERT_TO=michi` | `CONVERT_TO=michi` | `#ifdef CONVERT_TO_MICHI` |
|
| `michi` | `-e CONVERT_TO=michi` | `CONVERT_TO=michi` | `#ifdef CONVERT_TO_MICHI` |
|
||||||
|
|
||||||
### Proton C :id=proton_c
|
### Proton C :id=proton_c
|
||||||
@ -168,7 +171,7 @@ The Bonsai C4 only has one on-board LED (B2), and by default, both the Pro Micro
|
|||||||
#define B0 PAL_LINE(GPIOA, 9)
|
#define B0 PAL_LINE(GPIOA, 9)
|
||||||
```
|
```
|
||||||
|
|
||||||
### RP2040 Community Edition - Elite-Pi and Helios :id=rp2040_ce
|
### RP2040 Community Edition - Elite-Pi, Helios, and Liatris :id=rp2040_ce
|
||||||
|
|
||||||
Feature set currently identical to [Adafruit KB2040](#kb2040).
|
Feature set currently identical to [Adafruit KB2040](#kb2040).
|
||||||
|
|
||||||
@ -185,6 +188,7 @@ If a board currently supported in QMK uses an [Elite-C](https://keeb.io/products
|
|||||||
| [STeMCell](https://github.com/megamind4089/STeMCell) | `stemcell` |
|
| [STeMCell](https://github.com/megamind4089/STeMCell) | `stemcell` |
|
||||||
| [Elite-Pi](https://keeb.io/products/elite-pi-usb-c-pro-micro-replacement-rp2040) | `elite_pi` |
|
| [Elite-Pi](https://keeb.io/products/elite-pi-usb-c-pro-micro-replacement-rp2040) | `elite_pi` |
|
||||||
| [0xCB Helios](https://keeb.supply/products/0xcb-helios) | `helios` |
|
| [0xCB Helios](https://keeb.supply/products/0xcb-helios) | `helios` |
|
||||||
|
| [Liatris](https://splitkb.com/products/liatris) | `liatris` |
|
||||||
|
|
||||||
Converter summary:
|
Converter summary:
|
||||||
|
|
||||||
@ -194,6 +198,7 @@ Converter summary:
|
|||||||
| `rp2040_ce` | `-e CONVERT_TO=rp2040_ce` | `CONVERT_TO=rp2040_ce` | `#ifdef CONVERT_TO_RP2040_CE` |
|
| `rp2040_ce` | `-e CONVERT_TO=rp2040_ce` | `CONVERT_TO=rp2040_ce` | `#ifdef CONVERT_TO_RP2040_CE` |
|
||||||
| `elite_pi` | `-e CONVERT_TO=elite_pi` | `CONVERT_TO=elite_pi` | `#ifdef CONVERT_TO_ELITE_PI` |
|
| `elite_pi` | `-e CONVERT_TO=elite_pi` | `CONVERT_TO=elite_pi` | `#ifdef CONVERT_TO_ELITE_PI` |
|
||||||
| `helios` | `-e CONVERT_TO=helios` | `CONVERT_TO=helios` | `#ifdef CONVERT_TO_HELIOS` |
|
| `helios` | `-e CONVERT_TO=helios` | `CONVERT_TO=helios` | `#ifdef CONVERT_TO_HELIOS` |
|
||||||
|
| `liatris` | `-e CONVERT_TO=liatris` | `CONVERT_TO=liatris` | `#ifdef CONVERT_TO_LIATRIS` |
|
||||||
|
|
||||||
### STeMCell :id=stemcell_elite
|
### STeMCell :id=stemcell_elite
|
||||||
|
|
||||||
|
@ -57,78 +57,78 @@ susceptible to noise, you must choose a debounce method that will also mitigate
|
|||||||
if the scanning is slow, and you are using a timestamp-based algorithm, you might end up making a debouncing decision based on only two
|
if the scanning is slow, and you are using a timestamp-based algorithm, you might end up making a debouncing decision based on only two
|
||||||
sampled values, which will limit the noise-resistance of the algorithm.
|
sampled values, which will limit the noise-resistance of the algorithm.
|
||||||
* Currently all built-in debounce algorithms support timestamp-based debouncing only. In the future we might
|
* Currently all built-in debounce algorithms support timestamp-based debouncing only. In the future we might
|
||||||
implement cycles-based debouncing, and it will be selectable via a ```config.h``` macro.
|
implement cycles-based debouncing, and it will be selectable via a `config.h` macro.
|
||||||
|
|
||||||
2) Symmetric vs Asymmetric
|
2) Symmetric vs Asymmetric
|
||||||
* Symmetric - apply the same debouncing algorithm, to both key-up and key-down events.
|
* Symmetric - apply the same debouncing algorithm, to both key-up and key-down events.
|
||||||
* Recommended naming convention: ```sym_*```
|
* Recommended naming convention: `sym_*`
|
||||||
* Asymmetric - apply different debouncing algorithms to key-down and key-up events. E.g. Eager key-down, Defer key-up.
|
* Asymmetric - apply different debouncing algorithms to key-down and key-up events. E.g. Eager key-down, Defer key-up.
|
||||||
* Recommended naming convention: ```asym_*``` followed by details of the type of algorithm in use, in order, for key-down and then key-up
|
* Recommended naming convention: `asym_*` followed by details of the type of algorithm in use, in order, for key-down and then key-up
|
||||||
|
|
||||||
3) Eager vs Defer
|
3) Eager vs Defer
|
||||||
* Eager - any key change is reported immediately. All further inputs for DEBOUNCE ms are ignored.
|
* Eager - any key change is reported immediately. All further inputs for DEBOUNCE ms are ignored.
|
||||||
* Eager algorithms are not noise-resistant.
|
* Eager algorithms are not noise-resistant.
|
||||||
* Recommended naming conventions:
|
* Recommended naming conventions:
|
||||||
* ```sym_eager_*```
|
* `sym_eager_*`
|
||||||
* ```asym_eager_*_*```: key-down is using eager algorithm
|
* `asym_eager_*_*`: key-down is using eager algorithm
|
||||||
* ```asym_*_eager_*```: key-up is using eager algorithm
|
* `asym_*_eager_*`: key-up is using eager algorithm
|
||||||
* Defer - wait for no changes for DEBOUNCE ms before reporting change.
|
* Defer - wait for no changes for DEBOUNCE ms before reporting change.
|
||||||
* Defer algorithms are noise-resistant
|
* Defer algorithms are noise-resistant
|
||||||
* Recommended naming conventions:
|
* Recommended naming conventions:
|
||||||
* ```sym_defer_*```
|
* `sym_defer_*`
|
||||||
* ```asym_defer_*_*```: key-down is using defer algorithm
|
* `asym_defer_*_*`: key-down is using defer algorithm
|
||||||
* ```asym_*_defer_*```: key-up is using defer algorithm
|
* `asym_*_defer_*`: key-up is using defer algorithm
|
||||||
|
|
||||||
4) Global vs Per-Key vs Per-Row
|
4) Global vs Per-Key vs Per-Row
|
||||||
* Global - one timer for all keys. Any key change state affects global timer
|
* Global - one timer for all keys. Any key change state affects global timer
|
||||||
* Recommended naming convention: ```*_g```
|
* Recommended naming convention: `*_g`
|
||||||
* Per-key - one timer per key
|
* Per-key - one timer per key
|
||||||
* Recommended naming convention: ```*_pk```
|
* Recommended naming convention: `*_pk`
|
||||||
* Per-row - one timer per row
|
* Per-row - one timer per row
|
||||||
* Recommended naming convention: ```*_pr```
|
* Recommended naming convention: `*_pr`
|
||||||
* Per-key and per-row algorithms consume more resources (in terms of performance,
|
* Per-key and per-row algorithms consume more resources (in terms of performance,
|
||||||
and ram usage), but fast typists might prefer them over global.
|
and ram usage), but fast typists might prefer them over global.
|
||||||
|
|
||||||
## Debounce algorithms supported by QMK
|
## Supported Debounce Algorithms
|
||||||
|
|
||||||
QMK supports multiple debounce algorithms through its debounce API.
|
QMK supports multiple algorithms through its debounce API.
|
||||||
|
|
||||||
### Debounce selection
|
### Debounce Time
|
||||||
|
|
||||||
| DEBOUNCE_TYPE | Description | What else is needed |
|
Default debounce time is 5 milliseconds and it can be changed with the following line in `config.h`:
|
||||||
| ------------- | --------------------------------------------------- | ----------------------------- |
|
```
|
||||||
| Not defined | Use the default algorithm, currently sym_defer_g | Nothing |
|
#define DEBOUNCE 10
|
||||||
| custom | Use your own debounce code | ```SRC += debounce.c``` add your own debounce.c and implement necessary functions |
|
```
|
||||||
| Anything Else | Use another algorithm from quantum/debounce/* | Nothing |
|
?> Setting `DEBOUNCE` to `0` will disable this feature.
|
||||||
|
|
||||||
**Regarding split keyboards**:
|
### Debounce Method
|
||||||
The debounce code is compatible with split keyboards.
|
|
||||||
|
|
||||||
### Selecting an included debouncing method
|
Keyboards may select one of the core debounce methods by adding the following line into `rules.mk`:
|
||||||
Keyboards may select one of the already implemented debounce methods, by adding to ```rules.mk``` the following line:
|
|
||||||
```
|
```
|
||||||
DEBOUNCE_TYPE = <name of algorithm>
|
DEBOUNCE_TYPE = <name of algorithm>
|
||||||
```
|
```
|
||||||
Where name of algorithm is one of:
|
Name of algorithm is one of:
|
||||||
* ```sym_defer_g``` - debouncing per keyboard. On any state change, a global timer is set. When ```DEBOUNCE``` milliseconds of no changes has occurred, all input changes are pushed.
|
|
||||||
* This is the current default algorithm. This is the highest performance algorithm with lowest memory usage, and it's also noise-resistant.
|
|
||||||
* ```sym_eager_pr``` - debouncing per row. On any state change, response is immediate, followed by locking the row ```DEBOUNCE``` milliseconds of no further input for that row.
|
|
||||||
For use in keyboards where refreshing ```NUM_KEYS``` 8-bit counters is computationally expensive / low scan rate, and fingers usually only hit one row at a time. This could be
|
|
||||||
appropriate for the ErgoDox models; the matrix is rotated 90°, and hence its "rows" are really columns, and each finger only hits a single "row" at a time in normal use.
|
|
||||||
* ```sym_eager_pk``` - debouncing per key. On any state change, response is immediate, followed by ```DEBOUNCE``` milliseconds of no further input for that key
|
|
||||||
* ```sym_defer_pr``` - debouncing per row. On any state change, a per-row timer is set. When ```DEBOUNCE``` milliseconds of no changes have occurred on that row, the entire row is pushed. Can improve responsiveness over `sym_defer_g` while being less susceptible than per-key debouncers to noise.
|
|
||||||
* ```sym_defer_pk``` - debouncing per key. On any state change, a per-key timer is set. When ```DEBOUNCE``` milliseconds of no changes have occurred on that key, the key status change is pushed.
|
|
||||||
* ```asym_eager_defer_pk``` - debouncing per key. On a key-down state change, response is immediate, followed by ```DEBOUNCE``` milliseconds of no further input for that key. On a key-up state change, a per-key timer is set. When ```DEBOUNCE``` milliseconds of no changes have occurred on that key, the key-up status change is pushed.
|
|
||||||
|
|
||||||
### A couple algorithms that could be implemented in the future:
|
| Algorithm | Description |
|
||||||
* ```sym_defer_pr```
|
| --------------------- | ----------- |
|
||||||
* ```sym_eager_g```
|
| `sym_defer_g` | Debouncing per keyboard. On any state change, a global timer is set. When `DEBOUNCE` milliseconds of no changes has occurred, all input changes are pushed. This is the highest performance algorithm with lowest memory usage and is noise-resistant. |
|
||||||
|
| `sym_defer_pr` | Debouncing per row. On any state change, a per-row timer is set. When `DEBOUNCE` milliseconds of no changes have occurred on that row, the entire row is pushed. This can improve responsiveness over `sym_defer_g` while being less susceptible to noise than per-key algorithm. |
|
||||||
|
| `sym_defer_pk` | Debouncing per key. On any state change, a per-key timer is set. When `DEBOUNCE` milliseconds of no changes have occurred on that key, the key status change is pushed. |
|
||||||
|
| `sym_eager_pr` | Debouncing per row. On any state change, response is immediate, followed by `DEBOUNCE` milliseconds of no further input for that row. |
|
||||||
|
| `sym_eager_pk` | Debouncing per key. On any state change, response is immediate, followed by `DEBOUNCE` milliseconds of no further input for that key. |
|
||||||
|
| `asym_eager_defer_pk` | Debouncing per key. On a key-down state change, response is immediate, followed by `DEBOUNCE` milliseconds of no further input for that key. On a key-up state change, a per-key timer is set. When `DEBOUNCE` milliseconds of no changes have occurred on that key, the key-up status change is pushed. |
|
||||||
|
|
||||||
### Use your own debouncing code
|
?> `sym_defer_g` is the default if `DEBOUNCE_TYPE` is undefined.
|
||||||
You have the option to implement you own debouncing algorithm. To do this:
|
|
||||||
* Set ```DEBOUNCE_TYPE = custom``` in ```rules.mk```.
|
?> `sym_eager_pr` is suitable for use in keyboards where refreshing `NUM_KEYS` 8-bit counters is computationally expensive or has low scan rate while fingers usually hit one row at a time. This could be appropriate for the ErgoDox models where the matrix is rotated 90°. Hence its "rows" are really columns and each finger only hits a single "row" at a time with normal usage.
|
||||||
* Add ```SRC += debounce.c``` in ```rules.mk```
|
|
||||||
* Add your own ```debounce.c```. Look at current implementations in ```quantum/debounce``` for examples.
|
### Implementing your own debouncing code
|
||||||
|
|
||||||
|
You have the option to implement you own debouncing algorithm with the following steps:
|
||||||
|
|
||||||
|
* Set `DEBOUNCE_TYPE = custom` in `rules.mk`.
|
||||||
|
* Add `SRC += debounce.c` in `rules.mk`
|
||||||
|
* Implement your own `debounce.c`. See `quantum/debounce` for examples.
|
||||||
* Debouncing occurs after every raw matrix scan.
|
* Debouncing occurs after every raw matrix scan.
|
||||||
* Use num_rows rather than MATRIX_ROWS, so that split keyboards are supported correctly.
|
* Use num_rows instead of MATRIX_ROWS to support split keyboards correctly.
|
||||||
* If the algorithm might be applicable to other keyboards, please consider adding it to ```quantum/debounce```
|
* If your custom algorithm is applicable to other keyboards, please consider making a pull request.
|
||||||
|
@ -59,7 +59,7 @@ There are a number of hooks that you can use to add custom functionality and fee
|
|||||||
|
|
||||||
Note, that direction indicates which macro it is, with `1` being Macro 1, `-1` being Macro 2, and 0 being no macro.
|
Note, that direction indicates which macro it is, with `1` being Macro 1, `-1` being Macro 2, and 0 being no macro.
|
||||||
|
|
||||||
* `dynamic_macro_record_start_user(void)` - Triggered when you start recording a macro.
|
* `dynamic_macro_record_start_user(int8_t direction)` - Triggered when you start recording a macro.
|
||||||
* `dynamic_macro_play_user(int8_t direction)` - Triggered when you play back a macro.
|
* `dynamic_macro_play_user(int8_t direction)` - Triggered when you play back a macro.
|
||||||
* `dynamic_macro_record_key_user(int8_t direction, keyrecord_t *record)` - Triggered on each keypress while recording a macro.
|
* `dynamic_macro_record_key_user(int8_t direction, keyrecord_t *record)` - Triggered on each keypress while recording a macro.
|
||||||
* `dynamic_macro_record_end_user(int8_t direction)` - Triggered when the macro recording is stopped.
|
* `dynamic_macro_record_end_user(int8_t direction)` - Triggered when the macro recording is stopped.
|
||||||
|
@ -81,7 +81,7 @@ Your `keymap.c` will then need an encoder mapping defined (for four layers and t
|
|||||||
|
|
||||||
```c
|
```c
|
||||||
#if defined(ENCODER_MAP_ENABLE)
|
#if defined(ENCODER_MAP_ENABLE)
|
||||||
const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][2] = {
|
const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][NUM_DIRECTIONS] = {
|
||||||
[_BASE] = { ENCODER_CCW_CW(KC_MS_WH_UP, KC_MS_WH_DOWN), ENCODER_CCW_CW(KC_VOLD, KC_VOLU) },
|
[_BASE] = { ENCODER_CCW_CW(KC_MS_WH_UP, KC_MS_WH_DOWN), ENCODER_CCW_CW(KC_VOLD, KC_VOLU) },
|
||||||
[_LOWER] = { ENCODER_CCW_CW(RGB_HUD, RGB_HUI), ENCODER_CCW_CW(RGB_SAD, RGB_SAI) },
|
[_LOWER] = { ENCODER_CCW_CW(RGB_HUD, RGB_HUI), ENCODER_CCW_CW(RGB_SAD, RGB_SAI) },
|
||||||
[_RAISE] = { ENCODER_CCW_CW(RGB_VAD, RGB_VAI), ENCODER_CCW_CW(RGB_SPD, RGB_SPI) },
|
[_RAISE] = { ENCODER_CCW_CW(RGB_VAD, RGB_VAI), ENCODER_CCW_CW(RGB_SPD, RGB_SPI) },
|
||||||
@ -102,9 +102,9 @@ Using encoder mapping pumps events through the normal QMK keycode processing pip
|
|||||||
|
|
||||||
## Callbacks
|
## Callbacks
|
||||||
|
|
||||||
When not using `ENCODER_MAP_ENABLE = yes`, the callback functions can be inserted into your `<keyboard>.c`:
|
?> [**Default Behaviour**](https://github.com/qmk/qmk_firmware/blob/master/quantum/encoder.c#L79-#L98): all encoders installed will function as volume up (`KC_VOLU`) on clockwise rotation and volume down (`KC_VOLD`) on counter-clockwise rotation. If you do not wish to override this, no further configuration is necessary.
|
||||||
|
|
||||||
?> Those who are adding new keyboard support where encoders are enabled at the keyboard level should include basic encoder functionality at the keyboard level (`<keyboard>.c`) using the `encoder_update_kb()` function, that way it works for QMK Configuator users and exists in general.
|
If you would like the alter the default behaviour, and are not using `ENCODER_MAP_ENABLE = yes`, the callback functions can be inserted into your `<keyboard>.c`:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
bool encoder_update_kb(uint8_t index, bool clockwise) {
|
bool encoder_update_kb(uint8_t index, bool clockwise) {
|
||||||
@ -113,9 +113,9 @@ bool encoder_update_kb(uint8_t index, bool clockwise) {
|
|||||||
}
|
}
|
||||||
if (index == 0) { /* First encoder */
|
if (index == 0) { /* First encoder */
|
||||||
if (clockwise) {
|
if (clockwise) {
|
||||||
tap_code_delay(KC_VOLU, 10);
|
tap_code(KC_PGDN);
|
||||||
} else {
|
} else {
|
||||||
tap_code_delay(KC_VOLD, 10);
|
tap_code(KC_PGUP);
|
||||||
}
|
}
|
||||||
} else if (index == 1) { /* Second encoder */
|
} else if (index == 1) { /* Second encoder */
|
||||||
if (clockwise) {
|
if (clockwise) {
|
||||||
@ -134,9 +134,9 @@ or `keymap.c`:
|
|||||||
bool encoder_update_user(uint8_t index, bool clockwise) {
|
bool encoder_update_user(uint8_t index, bool clockwise) {
|
||||||
if (index == 0) { /* First encoder */
|
if (index == 0) { /* First encoder */
|
||||||
if (clockwise) {
|
if (clockwise) {
|
||||||
tap_code_delay(KC_VOLU, 10);
|
tap_code(KC_PGDN);
|
||||||
} else {
|
} else {
|
||||||
tap_code_delay(KC_VOLD, 10);
|
tap_code(KC_PGUP);
|
||||||
}
|
}
|
||||||
} else if (index == 1) { /* Second encoder */
|
} else if (index == 1) { /* Second encoder */
|
||||||
if (clockwise) {
|
if (clockwise) {
|
||||||
@ -149,7 +149,7 @@ bool encoder_update_user(uint8_t index, bool clockwise) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
!> If you return `true` in the keymap level `_user` function, it will allow the keyboard level encoder code to run on top of your own. Returning `false` will override the keyboard level function, if setup correctly. This is generally the safest option to avoid confusion.
|
!> If you return `true` in the keymap level `_user` function, it will allow the keyboard/core level encoder code to run on top of your own. Returning `false` will override the keyboard level function, if setup correctly. This is generally the safest option to avoid confusion.
|
||||||
|
|
||||||
## Hardware
|
## Hardware
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Key Overrides
|
# Key Overrides :id=key-overrides
|
||||||
|
|
||||||
Key overrides allow you to override modifier-key combinations to send a different modifier-key combination or perform completely custom actions. Don't want `shift` + `1` to type `!` on your computer? Use a key override to make your keyboard type something different when you press `shift` + `1`. The general behavior is like this: If `modifiers w` + `key x` are pressed, replace these keys with `modifiers y` + `key z` in the keyboard report.
|
Key overrides allow you to override modifier-key combinations to send a different modifier-key combination or perform completely custom actions. Don't want `shift` + `1` to type `!` on your computer? Use a key override to make your keyboard type something different when you press `shift` + `1`. The general behavior is like this: If `modifiers w` + `key x` are pressed, replace these keys with `modifiers y` + `key z` in the keyboard report.
|
||||||
|
|
||||||
@ -10,13 +10,13 @@ You can use key overrides in a similar way to momentary layer/fn keys to activat
|
|||||||
- Create custom shortcuts or change existing ones: E.g. Send `ctrl`+`shift`+`z` when `ctrl`+`y` is pressed.
|
- Create custom shortcuts or change existing ones: E.g. Send `ctrl`+`shift`+`z` when `ctrl`+`y` is pressed.
|
||||||
- Run custom code when `ctrl` + `alt` + `esc` is pressed.
|
- Run custom code when `ctrl` + `alt` + `esc` is pressed.
|
||||||
|
|
||||||
## Setup
|
## Setup :id=setup
|
||||||
|
|
||||||
To enable this feature, you need to add `KEY_OVERRIDE_ENABLE = yes` to your `rules.mk`.
|
To enable this feature, you need to add `KEY_OVERRIDE_ENABLE = yes` to your `rules.mk`.
|
||||||
|
|
||||||
Then, in your `keymap.c` file, you'll need to define the array `key_overrides`, which defines all key overrides to be used. Each override is a value of type `key_override_t`. The array `key_overrides` is `NULL`-terminated and contains pointers to `key_override_t` values (`const key_override_t **`).
|
Then, in your `keymap.c` file, you'll need to define the array `key_overrides`, which defines all key overrides to be used. Each override is a value of type `key_override_t`. The array `key_overrides` is `NULL`-terminated and contains pointers to `key_override_t` values (`const key_override_t **`).
|
||||||
|
|
||||||
## Creating Key Overrides
|
## Creating Key Overrides :id=creating-key-overrides
|
||||||
|
|
||||||
The `key_override_t` struct has many options that allow you to precisely tune your overrides. The full reference is shown below. Instead of manually creating a `key_override_t` value, it is recommended to use these dedicated initializers:
|
The `key_override_t` struct has many options that allow you to precisely tune your overrides. The full reference is shown below. Instead of manually creating a `key_override_t` value, it is recommended to use these dedicated initializers:
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ Additionally takes a bitmask `options` that specifies additional options. See `k
|
|||||||
|
|
||||||
For more customization possibilities, you may directly create a `key_override_t`, which allows you to customize even more behavior. Read further below for details and examples.
|
For more customization possibilities, you may directly create a `key_override_t`, which allows you to customize even more behavior. Read further below for details and examples.
|
||||||
|
|
||||||
## Simple Example
|
## Simple Example :id=simple-example
|
||||||
|
|
||||||
This shows how the mentioned example of sending `delete` when `shift` + `backspace` are pressed is realized:
|
This shows how the mentioned example of sending `delete` when `shift` + `backspace` are pressed is realized:
|
||||||
|
|
||||||
@ -48,9 +48,9 @@ const key_override_t **key_overrides = (const key_override_t *[]){
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
## Intermediate Difficulty Examples
|
## Intermediate Difficulty Examples :id=intermediate-difficulty-examples
|
||||||
|
|
||||||
### Media Controls & Screen Brightness
|
### Media Controls & Screen Brightness :id=media-controls-amp-screen-brightness
|
||||||
|
|
||||||
In this example a single key is configured to control media, volume and screen brightness by using key overrides.
|
In this example a single key is configured to control media, volume and screen brightness by using key overrides.
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ const key_override_t **key_overrides = (const key_override_t *[]){
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
### Flexible macOS-friendly Grave Escape
|
### Flexible macOS-friendly Grave Escape :id=flexible-macos-friendly-grave-escape
|
||||||
The [Grave Escape feature](feature_grave_esc.md) is limited in its configurability and has [bugs when used on macOS](feature_grave_esc.md#caveats). Key overrides can be used to achieve a similar functionality as Grave Escape, but with more customization and without bugs on macOS.
|
The [Grave Escape feature](feature_grave_esc.md) is limited in its configurability and has [bugs when used on macOS](feature_grave_esc.md#caveats). Key overrides can be used to achieve a similar functionality as Grave Escape, but with more customization and without bugs on macOS.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
@ -121,8 +121,8 @@ const key_override_t **key_overrides = (const key_override_t *[]){
|
|||||||
|
|
||||||
In addition to not encountering unexpected bugs on macOS, you can also change the behavior as you wish. Instead setting `GUI` + `ESC` = `` ` `` you may change it to an arbitrary other modifier, for example `Ctrl` + `ESC` = `` ` ``.
|
In addition to not encountering unexpected bugs on macOS, you can also change the behavior as you wish. Instead setting `GUI` + `ESC` = `` ` `` you may change it to an arbitrary other modifier, for example `Ctrl` + `ESC` = `` ` ``.
|
||||||
|
|
||||||
## Advanced Examples
|
## Advanced Examples :id=advanced-examples
|
||||||
### Modifiers as Layer Keys
|
### Modifiers as Layer Keys :id=modifiers-as-layer-keys
|
||||||
|
|
||||||
Do you really need a dedicated key to toggle your fn layer? With key overrides, perhaps not. This example shows how you can configure to use `rGUI` + `rAlt` (right GUI and right alt) to access a momentary layer like an fn layer. With this you completely eliminate the need to use a dedicated layer key. Of course the choice of modifier keys can be changed as needed, `rGUI` + `rAlt` is just an example here.
|
Do you really need a dedicated key to toggle your fn layer? With key overrides, perhaps not. This example shows how you can configure to use `rGUI` + `rAlt` (right GUI and right alt) to access a momentary layer like an fn layer. With this you completely eliminate the need to use a dedicated layer key. Of course the choice of modifier keys can be changed as needed, `rGUI` + `rAlt` is just an example here.
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ const key_override_t fn_override = {.trigger_mods = MOD_BIT(KC_RGUI) |
|
|||||||
.enabled = NULL};
|
.enabled = NULL};
|
||||||
```
|
```
|
||||||
|
|
||||||
## Keycodes
|
## Keycodes :id=keycodes
|
||||||
|
|
||||||
|Keycode |Aliases |Description |
|
|Keycode |Aliases |Description |
|
||||||
|------------------------|---------|----------------------|
|
|------------------------|---------|----------------------|
|
||||||
@ -158,7 +158,7 @@ const key_override_t fn_override = {.trigger_mods = MOD_BIT(KC_RGUI) |
|
|||||||
|`QK_KEY_OVERRIDE_ON` |`KO_ON` |Turn on key overrides |
|
|`QK_KEY_OVERRIDE_ON` |`KO_ON` |Turn on key overrides |
|
||||||
|`QK_KEY_OVERRIDE_OFF` |`KO_OFF` |Turn off key overrides|
|
|`QK_KEY_OVERRIDE_OFF` |`KO_OFF` |Turn off key overrides|
|
||||||
|
|
||||||
## Reference for `key_override_t`
|
## Reference for `key_override_t` :id=reference-for-key_override_t
|
||||||
|
|
||||||
Advanced users may need more customization than what is offered by the simple `ko_make` initializers. For this, directly create a `key_override_t` value and set all members. Below is a reference for all members of `key_override_t`.
|
Advanced users may need more customization than what is offered by the simple `ko_make` initializers. For this, directly create a `key_override_t` value and set all members. Below is a reference for all members of `key_override_t`.
|
||||||
|
|
||||||
@ -175,7 +175,7 @@ Advanced users may need more customization than what is offered by the simple `k
|
|||||||
| `void *context` | A context that will be passed to the custom action function. |
|
| `void *context` | A context that will be passed to the custom action function. |
|
||||||
| `bool *enabled` | If this points to false this override will not be used. Set to NULL to always have this override enabled. |
|
| `bool *enabled` | If this points to false this override will not be used. Set to NULL to always have this override enabled. |
|
||||||
|
|
||||||
### Reference for `ko_option_t`
|
## Reference for `ko_option_t` :id=reference-for-ko_option_t
|
||||||
|
|
||||||
Bitfield with various options controlling the behavior of a key override.
|
Bitfield with various options controlling the behavior of a key override.
|
||||||
|
|
||||||
@ -189,11 +189,11 @@ Bitfield with various options controlling the behavior of a key override.
|
|||||||
| `ko_option_no_reregister_trigger` | If set, the trigger key will never be registered again after the override is deactivated. |
|
| `ko_option_no_reregister_trigger` | If set, the trigger key will never be registered again after the override is deactivated. |
|
||||||
| `ko_options_default` | The default options used by the `ko_make_xxx` functions |
|
| `ko_options_default` | The default options used by the `ko_make_xxx` functions |
|
||||||
|
|
||||||
## For Advanced Users: Inner Workings
|
## For Advanced Users: Inner Workings :id=for-advanced-users-inner-workings
|
||||||
|
|
||||||
This section explains how a key override works in detail, explaining where each member of `key_override_t` comes into play. Understanding this is essential to be able to take full advantage of all the options offered by key overrides.
|
This section explains how a key override works in detail, explaining where each member of `key_override_t` comes into play. Understanding this is essential to be able to take full advantage of all the options offered by key overrides.
|
||||||
|
|
||||||
#### Activation
|
#### Activation :id=activation
|
||||||
|
|
||||||
When the necessary keys are pressed (`trigger_mods` + `trigger`), the override is 'activated' and the replacement key is registered in the keyboard report (`replacement`), while the `trigger` key is removed from the keyboard report. The trigger modifiers may also be removed from the keyboard report upon activation of an override (`suppressed_mods`). The override will not activate if any of the `negative_modifiers` are pressed.
|
When the necessary keys are pressed (`trigger_mods` + `trigger`), the override is 'activated' and the replacement key is registered in the keyboard report (`replacement`), while the `trigger` key is removed from the keyboard report. The trigger modifiers may also be removed from the keyboard report upon activation of an override (`suppressed_mods`). The override will not activate if any of the `negative_modifiers` are pressed.
|
||||||
|
|
||||||
@ -207,11 +207,11 @@ Use the `option` member to customize which of these events are allowed to activa
|
|||||||
|
|
||||||
In any case, a key override can only activate if the `trigger` key is the _last_ non-modifier key that was pressed down. This emulates the behavior of how standard OSes (macOS, Windows, Linux) handle normal key input (to understand: Hold down `a`, then also hold down `b`, then hold down `shift`; `B` will be typed but not `A`).
|
In any case, a key override can only activate if the `trigger` key is the _last_ non-modifier key that was pressed down. This emulates the behavior of how standard OSes (macOS, Windows, Linux) handle normal key input (to understand: Hold down `a`, then also hold down `b`, then hold down `shift`; `B` will be typed but not `A`).
|
||||||
|
|
||||||
#### Deactivation
|
#### Deactivation :id=deactivation
|
||||||
|
|
||||||
An override is 'deactivated' when one of the trigger keys (`trigger_mods`, `trigger`) is lifted, another non-modifier key is pressed down, or one of the `negative_modifiers` is pressed down. When an override deactivates, the `replacement` key is removed from the keyboard report, while the `suppressed_mods` that are still held down are re-added to the keyboard report. By default, the `trigger` key is re-added to the keyboard report if it is still held down and no other non-modifier key has been pressed since. This again emulates the behavior of how standard OSes handle normal key input (To understand: hold down `a`, then also hold down `b`, then also `shift`, then release `b`; `A` will not be typed even though you are holding the `a` and `shift` keys). Use the `option` field `ko_option_no_reregister_trigger` to prevent re-registering the trigger key in all cases.
|
An override is 'deactivated' when one of the trigger keys (`trigger_mods`, `trigger`) is lifted, another non-modifier key is pressed down, or one of the `negative_modifiers` is pressed down. When an override deactivates, the `replacement` key is removed from the keyboard report, while the `suppressed_mods` that are still held down are re-added to the keyboard report. By default, the `trigger` key is re-added to the keyboard report if it is still held down and no other non-modifier key has been pressed since. This again emulates the behavior of how standard OSes handle normal key input (To understand: hold down `a`, then also hold down `b`, then also `shift`, then release `b`; `A` will not be typed even though you are holding the `a` and `shift` keys). Use the `option` field `ko_option_no_reregister_trigger` to prevent re-registering the trigger key in all cases.
|
||||||
|
|
||||||
#### Key Repeat Delay
|
#### Key Repeat Delay :id=key-repeat-delay
|
||||||
|
|
||||||
A third way in which standard OS-handling of modifier-key input is emulated in key overrides is with a ['key repeat delay'](https://www.dummies.com/computers/pcs/set-your-keyboards-repeat-delay-and-repeat-rate/). To explain what this is, let's look at how normal keyboard input is handled by mainstream OSes again: If you hold down `a`, followed by `shift`, you will see the letter `a` is first typed, then for a short moment nothing is typed and then repeating `A`s are typed. Take note that, although shift is pressed down just after `a` is pressed, it takes a moment until `A` is typed. This is caused by the aforementioned key repeat delay, and it is a feature that prevents unwanted repeated characters from being typed.
|
A third way in which standard OS-handling of modifier-key input is emulated in key overrides is with a ['key repeat delay'](https://www.dummies.com/computers/pcs/set-your-keyboards-repeat-delay-and-repeat-rate/). To explain what this is, let's look at how normal keyboard input is handled by mainstream OSes again: If you hold down `a`, followed by `shift`, you will see the letter `a` is first typed, then for a short moment nothing is typed and then repeating `A`s are typed. Take note that, although shift is pressed down just after `a` is pressed, it takes a moment until `A` is typed. This is caused by the aforementioned key repeat delay, and it is a feature that prevents unwanted repeated characters from being typed.
|
||||||
|
|
||||||
@ -222,6 +222,6 @@ This applies equally to releasing a modifier: When you hold `shift`, then press
|
|||||||
The duration of the key repeat delay is controlled with the `KEY_OVERRIDE_REPEAT_DELAY` macro. Define this value in your `config.h` file to change it. It is 500ms by default.
|
The duration of the key repeat delay is controlled with the `KEY_OVERRIDE_REPEAT_DELAY` macro. Define this value in your `config.h` file to change it. It is 500ms by default.
|
||||||
|
|
||||||
|
|
||||||
## Difference to Combos
|
## Difference to Combos :id=difference-to-combos
|
||||||
|
|
||||||
Note that key overrides are very different from [combos](https://docs.qmk.fm/#/feature_combo). Combos require that you press down several keys almost _at the same time_ and can work with any combination of non-modifier keys. Key overrides work like keyboard shortcuts (e.g. `ctrl` + `z`): They take combinations of _multiple_ modifiers and _one_ non-modifier key to then perform some custom action. Key overrides are implemented with much care to behave just like normal keyboard shortcuts would in regards to the order of pressed keys, timing, and interacton with other pressed keys. There are a number of optional settings that can be used to really fine-tune the behavior of each key override as well. Using key overrides also does not delay key input for regular key presses, which inherently happens in combos and may be undesirable.
|
Note that key overrides are very different from [combos](https://docs.qmk.fm/#/feature_combo). Combos require that you press down several keys almost _at the same time_ and can work with any combination of non-modifier keys. Key overrides work like keyboard shortcuts (e.g. `ctrl` + `z`): They take combinations of _multiple_ modifiers and _one_ non-modifier key to then perform some custom action. Key overrides are implemented with much care to behave just like normal keyboard shortcuts would in regards to the order of pressed keys, timing, and interacton with other pressed keys. There are a number of optional settings that can be used to really fine-tune the behavior of each key override as well. Using key overrides also does not delay key input for regular key presses, which inherently happens in combos and may be undesirable.
|
||||||
|
@ -127,6 +127,54 @@ layer_state_t layer_state_set_user(layer_state_t state) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Example: Keycode to cycle through layers
|
||||||
|
|
||||||
|
This example shows how to implement a custom keycode to cycle through a range of layers.
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Define the keycode, `QK_USER` avoids collisions with existing keycodes
|
||||||
|
enum keycodes {
|
||||||
|
KC_CYCLE_LAYERS = QK_USER,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1st layer on the cycle
|
||||||
|
#define LAYER_CYCLE_START 0
|
||||||
|
// Last layer on the cycle
|
||||||
|
#define LAYER_CYCLE_END 4
|
||||||
|
|
||||||
|
// Add the behaviour of this new keycode
|
||||||
|
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
|
||||||
|
switch (keycode) {
|
||||||
|
case KC_CYCLE_LAYERS:
|
||||||
|
// Our logic will happen on presses, nothing is done on releases
|
||||||
|
if (!record->event.pressed) {
|
||||||
|
// We've already handled the keycode (doing nothing), let QMK know so no further code is run unnecessarily
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t current_layer = get_highest_layer(layer_state);
|
||||||
|
|
||||||
|
// Check if we are within the range, if not quit
|
||||||
|
if (curent_layer > LAYER_CYCLE_END || current_layer < LAYER_CYCLE_START) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t next_layer = current_layer + 1;
|
||||||
|
if (next_layer > LAYER_CYCLE_END) {
|
||||||
|
next_layer = LAYER_CYCLE_START;
|
||||||
|
}
|
||||||
|
layer_move(next_layer);
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Process other keycodes normally
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Place `KC_CYCLE_LAYERS` as a keycode in your keymap
|
||||||
|
```
|
||||||
|
|
||||||
Use the `IS_LAYER_ON_STATE(state, layer)` and `IS_LAYER_OFF_STATE(state, layer)` macros to check the status of a particular layer.
|
Use the `IS_LAYER_ON_STATE(state, layer)` and `IS_LAYER_OFF_STATE(state, layer)` macros to check the status of a particular layer.
|
||||||
|
|
||||||
Outside of `layer_state_set_*` functions, you can use the `IS_LAYER_ON(layer)` and `IS_LAYER_OFF(layer)` macros to check global layer state.
|
Outside of `layer_state_set_*` functions, you can use the `IS_LAYER_ON(layer)` and `IS_LAYER_OFF(layer)` macros to check global layer state.
|
||||||
|
@ -378,13 +378,7 @@ For inspiration and examples, check out the built-in effects under `quantum/led_
|
|||||||
|
|
||||||
## EEPROM storage :id=eeprom-storage
|
## EEPROM storage :id=eeprom-storage
|
||||||
|
|
||||||
The EEPROM for it is currently shared with the RGB Matrix system (it's generally assumed only one feature would be used at a time), but could be configured to use its own 32bit address with:
|
The EEPROM for it is currently shared with the RGB Matrix system (it's generally assumed only one feature would be used at a time).
|
||||||
|
|
||||||
```c
|
|
||||||
#define EECONFIG_LED_MATRIX (uint32_t *)28
|
|
||||||
```
|
|
||||||
|
|
||||||
Where `28` is an unused index from `eeconfig.h`.
|
|
||||||
|
|
||||||
### Direct Operation :id=direct-operation
|
### Direct Operation :id=direct-operation
|
||||||
|Function |Description |
|
|Function |Description |
|
||||||
|
@ -2,15 +2,18 @@
|
|||||||
|
|
||||||
## Supported Hardware
|
## Supported Hardware
|
||||||
|
|
||||||
OLED modules using SSD1306 or SH1106 driver ICs, communicating over I2C.
|
OLED modules using SSD1306, SH1106 or SH1107 driver ICs, communicating over I2C or SPI.
|
||||||
Tested combinations:
|
Tested combinations:
|
||||||
|
|
||||||
|IC |Size |Platform|Notes |
|
|IC |Size |Platform|Notes |
|
||||||
|---------|------|--------|------------------------|
|
|---------|-------|--------|------------------------|
|
||||||
|SSD1306 |128x32|AVR |Primary support |
|
|SSD1306 |128x32 |AVR |Primary support |
|
||||||
|SSD1306 |128x64|AVR |Verified working |
|
|SSD1306 |128x64 |AVR |Verified working |
|
||||||
|SSD1306 |128x32|Arm | |
|
|SSD1306 |128x32 |Arm | |
|
||||||
|SH1106 |128x64|AVR |No rotation or scrolling|
|
|SH1106 |128x64 |AVR |No scrolling |
|
||||||
|
|SH1107 |64x128 |AVR |No scrolling |
|
||||||
|
|SH1107 |64x128 |Arm |No scrolling |
|
||||||
|
|SH1107 |128x128|Arm |No scrolling |
|
||||||
|
|
||||||
Hardware configurations using Arm-based microcontrollers or different sizes of OLED modules may be compatible, but are untested.
|
Hardware configurations using Arm-based microcontrollers or different sizes of OLED modules may be compatible, but are untested.
|
||||||
|
|
||||||
@ -23,15 +26,26 @@ OLED_ENABLE = yes
|
|||||||
```
|
```
|
||||||
|
|
||||||
## OLED type
|
## OLED type
|
||||||
|OLED Driver |Supported Device |
|
|
||||||
|-------------------|---------------------------|
|
|OLED Driver |Supported Device |
|
||||||
|SSD1306 (default) |For both SSD1306 and SH1106|
|
|-------------------|------------------------------------|
|
||||||
|
|SSD1306 (default) |For both SSD1306, SH1106, and SH1107|
|
||||||
|
|
||||||
e.g.
|
e.g.
|
||||||
```make
|
```make
|
||||||
OLED_DRIVER = SSD1306
|
OLED_DRIVER = SSD1306
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|OLED Transport | |
|
||||||
|
|---------------|------------------------------------------------|
|
||||||
|
|i2c (default) | Uses I2C for communication with the OLED panel |
|
||||||
|
|spi | Uses SPI for communication with the OLED panel |
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
```make
|
||||||
|
OLED_TRANSPORT = i2c
|
||||||
|
```
|
||||||
|
|
||||||
Then in your `keymap.c` file, implement the OLED task call. This example assumes your keymap has three layers named `_QWERTY`, `_FN` and `_ADJ`:
|
Then in your `keymap.c` file, implement the OLED task call. This example assumes your keymap has three layers named `_QWERTY`, `_FN` and `_ADJ`:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
@ -159,32 +173,57 @@ These configuration options should be placed in `config.h`. Example:
|
|||||||
#define OLED_BRIGHTNESS 128
|
#define OLED_BRIGHTNESS 128
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|Define |Default |Description |
|
||||||
|
|---------------------------|-------------------------------|---------------------------------------------------------------------------------------------------------------------|
|
||||||
|
|`OLED_BRIGHTNESS` |`255` |The default brightness level of the OLED, from 0 to 255. |
|
||||||
|
|`OLED_COLUMN_OFFSET` |`0` |Shift output to the right this many pixels.<br />Useful for 128x64 displays centered on a 132x64 SH1106 IC. |
|
||||||
|
|`OLED_DISPLAY_CLOCK` |`0x80` |Set the display clock divide ratio/oscillator frequency. |
|
||||||
|
|`OLED_FONT_H` |`"glcdfont.c"` |The font code file to use for custom fonts |
|
||||||
|
|`OLED_FONT_START` |`0` |The starting character index for custom fonts |
|
||||||
|
|`OLED_FONT_END` |`223` |The ending character index for custom fonts |
|
||||||
|
|`OLED_FONT_WIDTH` |`6` |The font width |
|
||||||
|
|`OLED_FONT_HEIGHT` |`8` |The font height (untested) |
|
||||||
|
|`OLED_IC` |`OLED_IC_SSD1306` |Set to `OLED_IC_SH1106` or `OLED_IC_SH1107` if the corresponding controller chip is used. |
|
||||||
|
|`OLED_FADE_OUT` |*Not defined* |Enables fade out animation. Use together with `OLED_TIMEOUT`. |
|
||||||
|
|`OLED_FADE_OUT_INTERVAL` |`0` |The speed of fade out animation, from 0 to 15. Larger values are slower. |
|
||||||
|
|`OLED_SCROLL_TIMEOUT` |`0` |Scrolls the OLED screen after 0ms of OLED inactivity. Helps reduce OLED Burn-in. Set to 0 to disable. |
|
||||||
|
|`OLED_SCROLL_TIMEOUT_RIGHT`|*Not defined* |Scroll timeout direction is right when defined, left when undefined. |
|
||||||
|
|`OLED_TIMEOUT` |`60000` |Turns off the OLED screen after 60000ms of screen update inactivity. Helps reduce OLED Burn-in. Set to 0 to disable. |
|
||||||
|
|`OLED_UPDATE_INTERVAL` |`0` (`50` for split keyboards) |Set the time interval for updating the OLED display in ms. This will improve the matrix scan rate. |
|
||||||
|
|`OLED_UPDATE_PROCESS_LIMIT'|`1` |Set the number of dirty blocks to render per loop. Increasing may degrade performance. |
|
||||||
|
|
||||||
|
### I2C Configuration
|
||||||
|Define |Default |Description |
|
|Define |Default |Description |
|
||||||
|---------------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------|
|
|---------------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------|
|
||||||
|`OLED_DISPLAY_ADDRESS` |`0x3C` |The i2c address of the OLED Display |
|
|`OLED_DISPLAY_ADDRESS` |`0x3C` |The i2c address of the OLED Display |
|
||||||
|`OLED_FONT_H` |`"glcdfont.c"` |The font code file to use for custom fonts |
|
|
||||||
|`OLED_FONT_START` |`0` |The starting character index for custom fonts |
|
|
||||||
|`OLED_FONT_END` |`223` |The ending character index for custom fonts |
|
|
||||||
|`OLED_FONT_WIDTH` |`6` |The font width |
|
|
||||||
|`OLED_FONT_HEIGHT` |`8` |The font height (untested) |
|
|
||||||
|`OLED_TIMEOUT` |`60000` |Turns off the OLED screen after 60000ms of screen update inactivity. Helps reduce OLED Burn-in. Set to 0 to disable. |
|
|
||||||
|`OLED_FADE_OUT` |*Not defined* |Enables fade out animation. Use together with `OLED_TIMEOUT`. |
|
|
||||||
|`OLED_FADE_OUT_INTERVAL` |`0` |The speed of fade out animation, from 0 to 15. Larger values are slower. |
|
|
||||||
|`OLED_SCROLL_TIMEOUT` |`0` |Scrolls the OLED screen after 0ms of OLED inactivity. Helps reduce OLED Burn-in. Set to 0 to disable. |
|
|
||||||
|`OLED_SCROLL_TIMEOUT_RIGHT`|*Not defined* |Scroll timeout direction is right when defined, left when undefined. |
|
|
||||||
|`OLED_IC` |`OLED_IC_SSD1306`|Set to `OLED_IC_SH1106` if you're using the SH1106 OLED controller. |
|
|
||||||
|`OLED_COLUMN_OFFSET` |`0` |(SH1106 only.) Shift output to the right this many pixels.<br />Useful for 128x64 displays centered on a 132x64 SH1106 IC.|
|
|
||||||
|`OLED_BRIGHTNESS` |`255` |The default brightness level of the OLED, from 0 to 255. |
|
|
||||||
|`OLED_UPDATE_INTERVAL` |`0` |Set the time interval for updating the OLED display in ms. This will improve the matrix scan rate. |
|
|
||||||
|
|
||||||
## 128x64 & Custom sized OLED Displays
|
### SPI Configuration
|
||||||
|
|
||||||
The default display size for this feature is 128x32 and all necessary defines are precalculated with that in mind. We have added a define, `OLED_DISPLAY_128X64`, to switch all the values to be used in a 128x64 display, as well as added a custom define, `OLED_DISPLAY_CUSTOM`, that allows you to provide the necessary values to the driver.
|
|Define |Default |Description |
|
||||||
|
|---------------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
|`OLED_DC_PIN` | Required |The pin used for the DC connection of the OLED Display. |
|
||||||
|
|`OLED_CS_PIN` | Required |The pin used for the CS connection of the OLED Display. |
|
||||||
|
|`OLED_RST_PIN` | *Not defined* |The pin used for the RST connection of the OLED Display (may be left undefined if the RST pin is not connected). |
|
||||||
|
|`OLED_SPI_MODE` |`3` (default) |The SPI Mode for the OLED Display (not typically changed). |
|
||||||
|
|`OLED_SPI_DIVISOR` |`2` (default) |The SPI Multiplier to use for the OLED Display. |
|
||||||
|
|
||||||
|
## 128x64 & Custom sized OLED Displays
|
||||||
|
|
||||||
|
The default display size for this feature is 128x32, and the defaults are set with that in mind. However, there are a number of additional presets for common sizes that we have added. You can define one of these values to use the presets. If your display doesn't match one of these presets, you can define `OLED_DISPLAY_CUSTOM` to manually specify all of the values.
|
||||||
|
|
||||||
|
|Define |Default |Description |
|
||||||
|
|----------------------|---------------|---------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
|`OLED_DISPLAY_128X64` |*Not defined* |Changes the display defines for use with 128x64 displays. |
|
||||||
|
|`OLED_DISPLAY_64X32` |*Not defined* |Changes the display defines for use with 64x32 displays. |
|
||||||
|
|`OLED_DISPLAY_64X48` |*Not defined* |Changes the display defines for use with 64x48 displays. |
|
||||||
|
|`OLED_DISPLAY_64X128` |*Not defined* |Changes the display defines for use with 64x128 displays. |
|
||||||
|
|`OLED_DISPLAY_128X128`|*Not defined* |Changes the display defines for use with 128x128 displays. |
|
||||||
|
|`OLED_DISPLAY_CUSTOM` |*Not defined* |Changes the display defines for use with custom displays.<br>Requires user to implement the below defines. |
|
||||||
|
|
||||||
|
!> 64x128 and 128x128 displays default to the SH1107 IC type, as these heights are not supported by the other IC types.
|
||||||
|
|
||||||
|Define |Default |Description |
|
|Define |Default |Description |
|
||||||
|---------------------|---------------|----------------------------------------------------------------------------------------------------------------------------------------|
|
| --------------------|---------------|----------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|`OLED_DISPLAY_128X64`|*Not defined* |Changes the display defines for use with 128x64 displays. |
|
|
||||||
|`OLED_DISPLAY_CUSTOM`|*Not defined* |Changes the display defines for use with custom displays.<br>Requires user to implement the below defines. |
|
|
||||||
|`OLED_DISPLAY_WIDTH` |`128` |The width of the OLED display. |
|
|`OLED_DISPLAY_WIDTH` |`128` |The width of the OLED display. |
|
||||||
|`OLED_DISPLAY_HEIGHT`|`32` |The height of the OLED display. |
|
|`OLED_DISPLAY_HEIGHT`|`32` |The height of the OLED display. |
|
||||||
|`OLED_MATRIX_SIZE` |`512` |The local buffer size to allocate.<br>`(OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH)`. |
|
|`OLED_MATRIX_SIZE` |`512` |The local buffer size to allocate.<br>`(OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH)`. |
|
||||||
@ -192,14 +231,13 @@ These configuration options should be placed in `config.h`. Example:
|
|||||||
|`OLED_BLOCK_COUNT` |`16` |The number of blocks the display is divided into for dirty rendering.<br>`(sizeof(OLED_BLOCK_TYPE) * 8)`. |
|
|`OLED_BLOCK_COUNT` |`16` |The number of blocks the display is divided into for dirty rendering.<br>`(sizeof(OLED_BLOCK_TYPE) * 8)`. |
|
||||||
|`OLED_BLOCK_SIZE` |`32` |The size of each block for dirty rendering<br>`(OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)`. |
|
|`OLED_BLOCK_SIZE` |`32` |The size of each block for dirty rendering<br>`(OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)`. |
|
||||||
|`OLED_COM_PINS` |`COM_PINS_SEQ` |How the SSD1306 chip maps it's memory to display.<br>Options are `COM_PINS_SEQ`, `COM_PINS_ALT`, `COM_PINS_SEQ_LR`, & `COM_PINS_ALT_LR`.|
|
|`OLED_COM_PINS` |`COM_PINS_SEQ` |How the SSD1306 chip maps it's memory to display.<br>Options are `COM_PINS_SEQ`, `COM_PINS_ALT`, `COM_PINS_SEQ_LR`, & `COM_PINS_ALT_LR`.|
|
||||||
|
|`OLED_COM_PIN_COUNT` |*Not defined* |Number of COM pins supported by the controller.<br>If not defined, the value appropriate for the defined `OLED_IC` is used. |
|
||||||
|
|`OLED_COM_PIN_OFFSET`|`0` |Number of the first COM pin used by the OLED matrix. |
|
||||||
|`OLED_SOURCE_MAP` |`{ 0, ... N }` |Precalculated source array to use for mapping source buffer to target OLED memory in 90 degree rendering. |
|
|`OLED_SOURCE_MAP` |`{ 0, ... N }` |Precalculated source array to use for mapping source buffer to target OLED memory in 90 degree rendering. |
|
||||||
|`OLED_TARGET_MAP` |`{ 24, ... N }`|Precalculated target array to use for mapping source buffer to target OLED memory in 90 degree rendering. |
|
|`OLED_TARGET_MAP` |`{ 24, ... N }`|Precalculated target array to use for mapping source buffer to target OLED memory in 90 degree rendering. |
|
||||||
|
|
||||||
|
|
||||||
### 90 Degree Rotation - Technical Mumbo Jumbo
|
### 90 Degree Rotation - Technical Mumbo Jumbo
|
||||||
|
|
||||||
!> Rotation is unsupported on the SH1106.
|
|
||||||
|
|
||||||
```c
|
```c
|
||||||
// OLED Rotation enum values are flags
|
// OLED Rotation enum values are flags
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -210,7 +248,7 @@ typedef enum {
|
|||||||
} oled_rotation_t;
|
} oled_rotation_t;
|
||||||
```
|
```
|
||||||
|
|
||||||
OLED displays driven by SSD1306 drivers only natively support in hardware 0 degree and 180 degree rendering. This feature is done in software and not free. Using this feature will increase the time to calculate what data to send over i2c to the OLED. If you are strapped for cycles, this can cause keycodes to not register. In testing however, the rendering time on an ATmega32U4 board only went from 2ms to 5ms and keycodes not registering was only noticed once we hit 15ms.
|
OLED displays driven by SSD1306, SH1106 or SH1107 drivers only natively support in hardware 0 degree and 180 degree rendering. This feature is done in software and not free. Using this feature will increase the time to calculate what data to send over i2c to the OLED. If you are strapped for cycles, this can cause keycodes to not register. In testing however, the rendering time on an ATmega32U4 board only went from 2ms to 5ms and keycodes not registering was only noticed once we hit 15ms.
|
||||||
|
|
||||||
90 degree rotation is achieved by using bitwise operations to rotate each 8 block of memory and uses two precalculated arrays to remap buffer memory to OLED memory. The memory map defines are precalculated for remap performance and are calculated based on the display height, width, and block size. For example, in the 128x32 implementation with a `uint8_t` block type, we have a 64 byte block size. This gives us eight 8 byte blocks that need to be rotated and rendered. The OLED renders horizontally two 8 byte blocks before moving down a page, e.g:
|
90 degree rotation is achieved by using bitwise operations to rotate each 8 block of memory and uses two precalculated arrays to remap buffer memory to OLED memory. The memory map defines are precalculated for remap performance and are calculated based on the display height, width, and block size. For example, in the 128x32 implementation with a `uint8_t` block type, we have a 64 byte block size. This gives us eight 8 byte blocks that need to be rotated and rendered. The OLED renders horizontally two 8 byte blocks before moving down a page, e.g:
|
||||||
|
|
||||||
@ -232,6 +270,8 @@ However the local buffer is stored as if it was Height x Width display instead o
|
|||||||
|
|
||||||
So those precalculated arrays just index the memory offsets in the order in which each one iterates its data.
|
So those precalculated arrays just index the memory offsets in the order in which each one iterates its data.
|
||||||
|
|
||||||
|
Rotation on SH1106 and SH1107 is noticeably less efficient than on SSD1306, because these controllers do not support the “horizontal addressing mode”, which allows transferring the data for the whole rotated block at once; instead, separate address setup commands for every page in the block are required. The screen refresh time for SH1107 is therefore about 45% higher than for a same size screen with SSD1306 when using STM32 MCUs (on AVR the slowdown is about 20%, because the code which actually rotates the bitmap consumes more time).
|
||||||
|
|
||||||
## OLED API
|
## OLED API
|
||||||
|
|
||||||
```c
|
```c
|
||||||
@ -253,6 +293,11 @@ bool oled_init(oled_rotation_t rotation);
|
|||||||
oled_rotation_t oled_init_kb(oled_rotation_t rotation);
|
oled_rotation_t oled_init_kb(oled_rotation_t rotation);
|
||||||
oled_rotation_t oled_init_user(oled_rotation_t rotation);
|
oled_rotation_t oled_init_user(oled_rotation_t rotation);
|
||||||
|
|
||||||
|
// Send commands/data to screen
|
||||||
|
bool oled_send_cmd(const uint8_t *data, uint16_t size);
|
||||||
|
bool oled_send_cmd_P(const uint8_t *data, uint16_t size);
|
||||||
|
bool oled_send_data(const uint8_t *data, uint16_t size);
|
||||||
|
|
||||||
// Clears the display buffer, resets cursor position to 0, and sets the buffer to dirty for rendering
|
// Clears the display buffer, resets cursor position to 0, and sets the buffer to dirty for rendering
|
||||||
void oled_clear(void);
|
void oled_clear(void);
|
||||||
|
|
||||||
@ -386,7 +431,9 @@ uint8_t oled_max_chars(void);
|
|||||||
uint8_t oled_max_lines(void);
|
uint8_t oled_max_lines(void);
|
||||||
```
|
```
|
||||||
|
|
||||||
!> Scrolling and rotation are unsupported on the SH1106.
|
!> Scrolling is unsupported on the SH1106 and SH1107.
|
||||||
|
|
||||||
|
!> Scrolling does not work properly on the SSD1306 if the display width is smaller than 128.
|
||||||
|
|
||||||
## SSD1306.h Driver Conversion Guide
|
## SSD1306.h Driver Conversion Guide
|
||||||
|
|
||||||
|
@ -20,13 +20,13 @@ To use the ADNS 5050 sensor, add this to your `rules.mk`
|
|||||||
POINTING_DEVICE_DRIVER = adns5050
|
POINTING_DEVICE_DRIVER = adns5050
|
||||||
```
|
```
|
||||||
|
|
||||||
The ADNS 5050 sensor uses a serial type protocol for communication, and requires an additional light source.
|
The ADNS 5050 sensor uses a serial type protocol for communication, and requires an additional light source.
|
||||||
|
|
||||||
| Setting | Description | Default |
|
| Setting (`config.h`) | Description | Default |
|
||||||
| ------------------- | ------------------------------------------------------------------ | -------------------------- |
|
| -------------------- | ------------------------------------------------------------------ | -------------------------- |
|
||||||
| `ADNS5050_SCLK_PIN` | (Required) The pin connected to the clock pin of the sensor. | `POINTING_DEVICE_SCLK_PIN` |
|
| `ADNS5050_SCLK_PIN` | (Required) The pin connected to the clock pin of the sensor. | `POINTING_DEVICE_SCLK_PIN` |
|
||||||
| `ADNS5050_SDIO_PIN` | (Required) The pin connected to the data pin of the sensor. | `POINTING_DEVICE_SDIO_PIN` |
|
| `ADNS5050_SDIO_PIN` | (Required) The pin connected to the data pin of the sensor. | `POINTING_DEVICE_SDIO_PIN` |
|
||||||
| `ADNS5050_CS_PIN` | (Required) The pin connected to the Chip Select pin of the sensor. | `POINTING_DEVICE_CS_PIN` |
|
| `ADNS5050_CS_PIN` | (Required) The pin connected to the Chip Select pin of the sensor. | `POINTING_DEVICE_CS_PIN` |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -40,9 +40,9 @@ To use the ADNS 9800 sensor, add this to your `rules.mk`
|
|||||||
POINTING_DEVICE_DRIVER = adns9800
|
POINTING_DEVICE_DRIVER = adns9800
|
||||||
```
|
```
|
||||||
|
|
||||||
The ADNS 9800 is an SPI driven optical sensor, that uses laser output for surface tracking.
|
The ADNS 9800 is an SPI driven optical sensor, that uses laser output for surface tracking.
|
||||||
|
|
||||||
| Setting | Description | Default |
|
| Setting (`config.h`) | Description | Default |
|
||||||
| ----------------------- | ---------------------------------------------------------------------- | ------------------------ |
|
| ----------------------- | ---------------------------------------------------------------------- | ------------------------ |
|
||||||
| `ADNS9800_CLOCK_SPEED` | (Optional) Sets the clock speed that the sensor runs at. | `2000000` |
|
| `ADNS9800_CLOCK_SPEED` | (Optional) Sets the clock speed that the sensor runs at. | `2000000` |
|
||||||
| `ADNS9800_SPI_LSBFIRST` | (Optional) Sets the Least/Most Significant Byte First setting for SPI. | `false` |
|
| `ADNS9800_SPI_LSBFIRST` | (Optional) Sets the Least/Most Significant Byte First setting for SPI. | `false` |
|
||||||
@ -63,7 +63,7 @@ POINTING_DEVICE_DRIVER = analog_joystick
|
|||||||
|
|
||||||
The Analog Joystick is an analog (ADC) driven sensor. There are a variety of joysticks that you can use for this.
|
The Analog Joystick is an analog (ADC) driven sensor. There are a variety of joysticks that you can use for this.
|
||||||
|
|
||||||
| Setting | Description | Default |
|
| Setting (`config.h`) | Description | Default |
|
||||||
| --------------------------------- | -------------------------------------------------------------------------- | ------------- |
|
| --------------------------------- | -------------------------------------------------------------------------- | ------------- |
|
||||||
| `ANALOG_JOYSTICK_X_AXIS_PIN` | (Required) The pin used for the vertical/X axis. | _not defined_ |
|
| `ANALOG_JOYSTICK_X_AXIS_PIN` | (Required) The pin used for the vertical/X axis. | _not defined_ |
|
||||||
| `ANALOG_JOYSTICK_Y_AXIS_PIN` | (Required) The pin used for the horizontal/Y axis. | _not defined_ |
|
| `ANALOG_JOYSTICK_Y_AXIS_PIN` | (Required) The pin used for the horizontal/Y axis. | _not defined_ |
|
||||||
@ -153,7 +153,7 @@ Additionally, `POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE` is supported in thi
|
|||||||
|
|
||||||
#### Relative mode gestures
|
#### Relative mode gestures
|
||||||
|
|
||||||
| Gesture Setting | Description | Default |
|
| Gesture Setting (`config.h`) | Description | Default |
|
||||||
| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
||||||
| `CIRQUE_PINNACLE_TAP_ENABLE` | (Optional) Enable tap to "left click". Works on both sides of a split keyboard. | _not defined_ |
|
| `CIRQUE_PINNACLE_TAP_ENABLE` | (Optional) Enable tap to "left click". Works on both sides of a split keyboard. | _not defined_ |
|
||||||
| `CIRQUE_PINNACLE_SECONDARY_TAP_ENABLE` | (Optional) Tap in upper right corner (half of the finger needs to be outside of the trackpad) of the trackpad will result in "right click". `CIRQUE_PINNACLE_TAP_ENABLE` must be enabled. | _not defined_ |
|
| `CIRQUE_PINNACLE_SECONDARY_TAP_ENABLE` | (Optional) Tap in upper right corner (half of the finger needs to be outside of the trackpad) of the trackpad will result in "right click". `CIRQUE_PINNACLE_TAP_ENABLE` must be enabled. | _not defined_ |
|
||||||
@ -172,10 +172,10 @@ POINTING_DEVICE_DRIVER = paw3204
|
|||||||
|
|
||||||
The paw 3204 sensor uses a serial type protocol for communication, and requires an additional light source.
|
The paw 3204 sensor uses a serial type protocol for communication, and requires an additional light source.
|
||||||
|
|
||||||
| Setting | Description | Default |
|
| Setting (`config.h`) | Description | Default |
|
||||||
| ------------------ |--------------------------------------------------------------- | -------------------------- |
|
| -------------------- |--------------------------------------------------------------- | -------------------------- |
|
||||||
| `PAW3204_SCLK_PIN` | (Required) The pin connected to the clock pin of the sensor. | `POINTING_DEVICE_SCLK_PIN` |
|
| `PAW3204_SCLK_PIN` | (Required) The pin connected to the clock pin of the sensor. | `POINTING_DEVICE_SCLK_PIN` |
|
||||||
| `PAW3204_SDIO_PIN` | (Required) The pin connected to the data pin of the sensor. | `POINTING_DEVICE_SDIO_PIN` |
|
| `PAW3204_SDIO_PIN` | (Required) The pin connected to the data pin of the sensor. | `POINTING_DEVICE_SDIO_PIN` |
|
||||||
|
|
||||||
The CPI range is 400-1600, with supported values of (400, 500, 600, 800, 1000, 1200 and 1600). Defaults to 1000 CPI.
|
The CPI range is 400-1600, with supported values of (400, 500, 600, 800, 1000, 1200 and 1600). Defaults to 1000 CPI.
|
||||||
|
|
||||||
@ -189,7 +189,7 @@ POINTING_DEVICE_DRIVER = pimoroni_trackball
|
|||||||
|
|
||||||
The Pimoroni Trackball module is a I2C based breakout board with an RGB enable trackball.
|
The Pimoroni Trackball module is a I2C based breakout board with an RGB enable trackball.
|
||||||
|
|
||||||
| Setting | Description | Default |
|
| Setting (`config.h`) | Description | Default |
|
||||||
| ------------------------------------ | ---------------------------------------------------------------------------------- | ------- |
|
| ------------------------------------ | ---------------------------------------------------------------------------------- | ------- |
|
||||||
| `PIMORONI_TRACKBALL_ADDRESS` | (Required) Sets the I2C Address for the Pimoroni Trackball. | `0x0A` |
|
| `PIMORONI_TRACKBALL_ADDRESS` | (Required) Sets the I2C Address for the Pimoroni Trackball. | `0x0A` |
|
||||||
| `PIMORONI_TRACKBALL_TIMEOUT` | (Optional) The timeout for i2c communication with the trackball in milliseconds. | `100` |
|
| `PIMORONI_TRACKBALL_TIMEOUT` | (Optional) The timeout for i2c communication with the trackball in milliseconds. | `100` |
|
||||||
@ -197,6 +197,24 @@ The Pimoroni Trackball module is a I2C based breakout board with an RGB enable t
|
|||||||
| `PIMORONI_TRACKBALL_DEBOUNCE_CYCLES` | (Optional) The number of scan cycles used for debouncing on the ball press. | `20` |
|
| `PIMORONI_TRACKBALL_DEBOUNCE_CYCLES` | (Optional) The number of scan cycles used for debouncing on the ball press. | `20` |
|
||||||
| `PIMORONI_TRACKBALL_ERROR_COUNT` | (Optional) Specifies the number of read/write errors until the sensor is disabled. | `10` |
|
| `PIMORONI_TRACKBALL_ERROR_COUNT` | (Optional) Specifies the number of read/write errors until the sensor is disabled. | `10` |
|
||||||
|
|
||||||
|
### PMW3320 Sensor
|
||||||
|
|
||||||
|
To use the PMW3320 sensor, add this to your `rules.mk`
|
||||||
|
|
||||||
|
```make
|
||||||
|
POINTING_DEVICE_DRIVER = pmw3320
|
||||||
|
```
|
||||||
|
|
||||||
|
The PMW3320 sensor uses a serial type protocol for communication, and requires an additional light source (it could work without one, but expect it to be out of service early).
|
||||||
|
|
||||||
|
| Setting | Description | Default |
|
||||||
|
| ------------------- | ------------------------------------------------------------------- | -------------------------- |
|
||||||
|
| `PMW3320_SCLK_PIN` | (Required) The pin connected to the clock pin of the sensor. | `POINTING_DEVICE_SCLK_PIN` |
|
||||||
|
| `PMW3320_SDIO_PIN` | (Required) The pin connected to the data pin of the sensor. | `POINTING_DEVICE_SDIO_PIN` |
|
||||||
|
| `PMW3320_CS_PIN` | (Required) The pin connected to the cable select pin of the sensor. | `POINTING_DEVICE_CS_PIN` |
|
||||||
|
|
||||||
|
The CPI range is 500-3500, in increments of 250. Defaults to 1000 CPI.
|
||||||
|
|
||||||
### PMW 3360 and PMW 3389 Sensor
|
### PMW 3360 and PMW 3389 Sensor
|
||||||
|
|
||||||
This drivers supports both the PMW 3360 and PMW 3389 sensor as well as multiple sensors of the same type _per_ controller, so 2 can be attached at the same side for split keyboards (or unsplit keyboards).
|
This drivers supports both the PMW 3360 and PMW 3389 sensor as well as multiple sensors of the same type _per_ controller, so 2 can be attached at the same side for split keyboards (or unsplit keyboards).
|
||||||
@ -220,7 +238,7 @@ The CPI range is 50-16000, in increments of 50. Defaults to 2000 CPI.
|
|||||||
Both PMW 3360 and PMW 3389 are SPI driven optical sensors, that use a built in IR LED for surface tracking.
|
Both PMW 3360 and PMW 3389 are SPI driven optical sensors, that use a built in IR LED for surface tracking.
|
||||||
If you have different CS wiring on each half you can use `PMW33XX_CS_PIN_RIGHT` or `PMW33XX_CS_PINS_RIGHT` in combination with `PMW33XX_CS_PIN` or `PMW33XX_CS_PINS` to configure both sides independently. If `_RIGHT` values aren't provided, they default to be the same as the left ones.
|
If you have different CS wiring on each half you can use `PMW33XX_CS_PIN_RIGHT` or `PMW33XX_CS_PINS_RIGHT` in combination with `PMW33XX_CS_PIN` or `PMW33XX_CS_PINS` to configure both sides independently. If `_RIGHT` values aren't provided, they default to be the same as the left ones.
|
||||||
|
|
||||||
| Setting | Description | Default |
|
| Setting (`config.h`) | Description | Default |
|
||||||
| ---------------------------- | ------------------------------------------------------------------------------------------- | ------------------------ |
|
| ---------------------------- | ------------------------------------------------------------------------------------------- | ------------------------ |
|
||||||
| `PMW33XX_CS_PIN` | (Required) Sets the Chip Select pin connected to the sensor. | `POINTING_DEVICE_CS_PIN` |
|
| `PMW33XX_CS_PIN` | (Required) Sets the Chip Select pin connected to the sensor. | `POINTING_DEVICE_CS_PIN` |
|
||||||
| `PMW33XX_CS_PINS` | (Alternative) Sets the Chip Select pins connected to multiple sensors. | `{PMW33XX_CS_PIN}` |
|
| `PMW33XX_CS_PINS` | (Alternative) Sets the Chip Select pins connected to multiple sensors. | `{PMW33XX_CS_PIN}` |
|
||||||
@ -434,6 +452,75 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
|
|||||||
|
|
||||||
This allows you to toggle between scrolling and cursor movement by pressing the DRAG_SCROLL key.
|
This allows you to toggle between scrolling and cursor movement by pressing the DRAG_SCROLL key.
|
||||||
|
|
||||||
|
### Advanced Drag Scroll
|
||||||
|
|
||||||
|
Sometimes, like with the Cirque trackpad, you will run into issues where the scrolling may be too fast.
|
||||||
|
|
||||||
|
Here is a slightly more advanced example of drag scrolling. You will be able to change the scroll speed based on the values in set in `SCROLL_DIVISOR_H` and `SCROLL_DIVISOR_V`. This bit of code is also set up so that instead of toggling the scrolling state with set_scrolling = !set_scrolling, the set_scrolling variable is set directly to record->event.pressed. This way, the drag scrolling will only be active while the DRAG_SCROLL button is held down.
|
||||||
|
|
||||||
|
```c
|
||||||
|
enum custom_keycodes {
|
||||||
|
DRAG_SCROLL = SAFE_RANGE,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool set_scrolling = false;
|
||||||
|
|
||||||
|
// Modify these values to adjust the scrolling speed
|
||||||
|
#define SCROLL_DIVISOR_H 8.0
|
||||||
|
#define SCROLL_DIVISOR_V 8.0
|
||||||
|
|
||||||
|
// Variables to store accumulated scroll values
|
||||||
|
float scroll_accumulated_h = 0;
|
||||||
|
float scroll_accumulated_v = 0;
|
||||||
|
|
||||||
|
// Function to handle mouse reports and perform drag scrolling
|
||||||
|
report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) {
|
||||||
|
// Check if drag scrolling is active
|
||||||
|
if (set_scrolling) {
|
||||||
|
// Calculate and accumulate scroll values based on mouse movement and divisors
|
||||||
|
scroll_accumulated_h += (float)mouse_report.x / SCROLL_DIVISOR_H;
|
||||||
|
scroll_accumulated_v += (float)mouse_report.y / SCROLL_DIVISOR_V;
|
||||||
|
|
||||||
|
// Assign integer parts of accumulated scroll values to the mouse report
|
||||||
|
mouse_report.h = (int8_t)scroll_accumulated_h;
|
||||||
|
mouse_report.v = (int8_t)scroll_accumulated_v;
|
||||||
|
|
||||||
|
// Update accumulated scroll values by subtracting the integer parts
|
||||||
|
scroll_accumulated_h -= (int8_t)scroll_accumulated_h;
|
||||||
|
scroll_accumulated_v -= (int8_t)scroll_accumulated_v;
|
||||||
|
|
||||||
|
// Clear the X and Y values of the mouse report
|
||||||
|
mouse_report.x = 0;
|
||||||
|
mouse_report.y = 0;
|
||||||
|
}
|
||||||
|
return mouse_report;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to handle key events and enable/disable drag scrolling
|
||||||
|
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
|
||||||
|
switch (keycode) {
|
||||||
|
case DRAG_SCROLL:
|
||||||
|
// Toggle set_scrolling when DRAG_SCROLL key is pressed or released
|
||||||
|
set_scrolling = record->event.pressed;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to handle layer changes and disable drag scrolling when not in AUTO_MOUSE_DEFAULT_LAYER
|
||||||
|
layer_state_t layer_state_set_user(layer_state_t state) {
|
||||||
|
// Disable set_scrolling if the current layer is not the AUTO_MOUSE_DEFAULT_LAYER
|
||||||
|
if (get_highest_layer(state) != AUTO_MOUSE_DEFAULT_LAYER) {
|
||||||
|
set_scrolling = false;
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Split Examples
|
## Split Examples
|
||||||
|
|
||||||
The following examples make use the `SPLIT_POINTING_ENABLE` functionality and show how to manipulate the mouse report for a scrolling mode.
|
The following examples make use the `SPLIT_POINTING_ENABLE` functionality and show how to manipulate the mouse report for a scrolling mode.
|
||||||
@ -602,6 +689,10 @@ There are several functions that allow for more advanced interaction with the au
|
|||||||
| `auto_mouse_layer_off(void)` | Disable target layer if appropriate will call (makes call to `layer_state_set`) | | `void`(None) |
|
| `auto_mouse_layer_off(void)` | Disable target layer if appropriate will call (makes call to `layer_state_set`) | | `void`(None) |
|
||||||
| `auto_mouse_toggle(void)` | Toggle on/off target toggle state (disables layer deactivation when true) | | `void`(None) |
|
| `auto_mouse_toggle(void)` | Toggle on/off target toggle state (disables layer deactivation when true) | | `void`(None) |
|
||||||
| `get_auto_mouse_toggle(void)` | Return value of toggling state variable | | `bool` |
|
| `get_auto_mouse_toggle(void)` | Return value of toggling state variable | | `bool` |
|
||||||
|
| `set_auto_mouse_timeout(uint16_t timeout)` | Change/set the timeout for turing off the layer | | `void`(None) |
|
||||||
|
| `get_auto_mouse_timeout(void)` | Return the current timeout for turing off the layer | | `uint16_t` |
|
||||||
|
| `set_auto_mouse_debounce(uint16_t timeout)` | Change/set the debounce for preventing layer activation | | `void`(None) |
|
||||||
|
| `get_auto_mouse_debounce(void)` | Return the current debounce for preventing layer activation | | `uint8_t` |
|
||||||
|
|
||||||
_NOTES:_
|
_NOTES:_
|
||||||
- _Due to the nature of how some functions work, the `auto_mouse_trigger_reset`, and `auto_mouse_layer_off` functions should never be called in the `layer_state_set_*` stack as this can cause indefinite loops._
|
- _Due to the nature of how some functions work, the `auto_mouse_trigger_reset`, and `auto_mouse_layer_off` functions should never be called in the `layer_state_set_*` stack as this can cause indefinite loops._
|
||||||
@ -713,7 +804,7 @@ _Note: The Cirque pinnacle track pad already implements a custom activation func
|
|||||||
When using a custom pointing device (overwriting `pointing_device_task`) the following code should be somewhere in the `pointing_device_task_*` stack:
|
When using a custom pointing device (overwriting `pointing_device_task`) the following code should be somewhere in the `pointing_device_task_*` stack:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void pointing_device_task(void) {
|
bool pointing_device_task(void) {
|
||||||
//...Custom pointing device task code
|
//...Custom pointing device task code
|
||||||
|
|
||||||
// handle automatic mouse layer (needs report_mouse_t as input)
|
// handle automatic mouse layer (needs report_mouse_t as input)
|
||||||
@ -721,7 +812,7 @@ void pointing_device_task(void) {
|
|||||||
|
|
||||||
//...More custom pointing device task code
|
//...More custom pointing device task code
|
||||||
|
|
||||||
pointing_device_send();
|
return pointing_device_send();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
457
docs/feature_repeat_key.md
Normal file
457
docs/feature_repeat_key.md
Normal file
@ -0,0 +1,457 @@
|
|||||||
|
# Repeat Key
|
||||||
|
|
||||||
|
The Repeat Key performs the action of the last pressed key. Tapping the Repeat
|
||||||
|
Key after tapping the <kbd>Z</kbd> key types another "`z`." This is useful for
|
||||||
|
typing doubled letters, like the `z` in "`dazzle`": a double tap on <kbd>Z</kbd>
|
||||||
|
can instead be a roll from <kbd>Z</kbd> to <kbd>Repeat</kbd>, which is
|
||||||
|
potentially faster and more comfortable. The Repeat Key is also useful for
|
||||||
|
hotkeys, like repeating Ctrl + Shift + Right Arrow to select by word.
|
||||||
|
|
||||||
|
Repeat Key remembers mods that were active with the last key press. These mods
|
||||||
|
are combined with any additional mods while pressing the Repeat Key. If the last
|
||||||
|
press key was <kbd>Ctrl</kbd> + <kbd>Z</kbd>, then <kbd>Shift</kbd> +
|
||||||
|
<kbd>Repeat</kbd> performs Ctrl + Shift + `Z`.
|
||||||
|
|
||||||
|
## How do I enable Repeat Key
|
||||||
|
|
||||||
|
In your `rules.mk`, add:
|
||||||
|
|
||||||
|
```make
|
||||||
|
REPEAT_KEY_ENABLE = yes
|
||||||
|
```
|
||||||
|
|
||||||
|
Then pick a key in your keymap and assign it the keycode `QK_REPEAT_KEY` (short
|
||||||
|
alias `QK_REP`). Optionally, use the keycode `QK_ALT_REPEAT_KEY` (short alias
|
||||||
|
`QK_AREP`) on another key.
|
||||||
|
|
||||||
|
## Keycodes
|
||||||
|
|
||||||
|
|Keycode |Aliases |Description |
|
||||||
|
|-----------------------|---------|-------------------------------------|
|
||||||
|
|`QK_REPEAT_KEY` |`QK_REP` |Repeat the last pressed key |
|
||||||
|
|`QK_ALT_REPEAT_KEY` |`QK_AREP`|Perform alternate of the last key |
|
||||||
|
|
||||||
|
## Alternate Repeating
|
||||||
|
|
||||||
|
The Alternate Repeat Key performs the "alternate" action of the last pressed key
|
||||||
|
if it is defined. By default, Alternate Repeat is defined for navigation keys to
|
||||||
|
act in the reverse direction. When the last key is the common "select by word"
|
||||||
|
hotkey Ctrl + Shift + Right Arrow, the Alternate Repeat Key performs Ctrl +
|
||||||
|
Shift + Left Arrow, which together with the Repeat Key enables convenient
|
||||||
|
selection by words in either direction.
|
||||||
|
|
||||||
|
Alternate Repeat is enabled with the Repeat Key by default. Optionally, to
|
||||||
|
reduce firmware size, Alternate Repeat may be disabled by adding in config.h:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define NO_ALT_REPEAT_KEY
|
||||||
|
```
|
||||||
|
|
||||||
|
The following alternate keys are defined by default. See
|
||||||
|
`get_alt_repeat_key_keycode_user()` below for how to change or add to these
|
||||||
|
definitions. Where it makes sense, these definitions also include combinations
|
||||||
|
with mods, like Ctrl + Left ↔ Ctrl + Right Arrow.
|
||||||
|
|
||||||
|
**Navigation**
|
||||||
|
|
||||||
|
|Keycodes |Description |
|
||||||
|
|-----------------------------------|-----------------------------------|
|
||||||
|
|`KC_LEFT` ↔ `KC_RGHT` | Left ↔ Right Arrow |
|
||||||
|
|`KC_UP` ↔ `KC_DOWN` | Up ↔ Down Arrow |
|
||||||
|
|`KC_HOME` ↔ `KC_END` | Home ↔ End |
|
||||||
|
|`KC_PGUP` ↔ `KC_PGDN` | Page Up ↔ Page Down |
|
||||||
|
|`KC_MS_L` ↔ `KC_MS_R` | Mouse Cursor Left ↔ Right |
|
||||||
|
|`KC_MS_U` ↔ `KC_MS_D` | Mouse Cursor Up ↔ Down |
|
||||||
|
|`KC_WH_L` ↔ `KC_WH_R` | Mouse Wheel Left ↔ Right |
|
||||||
|
|`KC_WH_U` ↔ `KC_WH_D` | Mouse Wheel Up ↔ Down |
|
||||||
|
|
||||||
|
**Misc**
|
||||||
|
|
||||||
|
|Keycodes |Description |
|
||||||
|
|-----------------------------------|-----------------------------------|
|
||||||
|
|`KC_BSPC` ↔ `KC_DEL` | Backspace ↔ Delete |
|
||||||
|
|`KC_LBRC` ↔ `KC_RBRC` | `[` ↔ `]` |
|
||||||
|
|`KC_LCBR` ↔ `KC_RCBR` | `{` ↔ `}` |
|
||||||
|
|
||||||
|
**Media**
|
||||||
|
|
||||||
|
|Keycodes |Description |
|
||||||
|
|-----------------------------------|-----------------------------------|
|
||||||
|
|`KC_WBAK` ↔ `KC_WFWD` | Browser Back ↔ Forward |
|
||||||
|
|`KC_MNXT` ↔ `KC_MPRV` | Next ↔ Previous Media Track |
|
||||||
|
|`KC_MFFD` ↔ `KC_MRWD` | Fast Forward ↔ Rewind Media |
|
||||||
|
|`KC_VOLU` ↔ `KC_VOLD` | Volume Up ↔ Down |
|
||||||
|
|`KC_BRIU` ↔ `KC_BRID` | Brightness Up ↔ Down |
|
||||||
|
|
||||||
|
**Hotkeys in Vim, Emacs, and other programs**
|
||||||
|
|
||||||
|
|Keycodes |Description |
|
||||||
|
|-----------------------------------|-----------------------------------|
|
||||||
|
|mod + `KC_F` ↔ mod + `KC_B` | Forward ↔ Backward |
|
||||||
|
|mod + `KC_D` ↔ mod + `KC_U` | Down ↔ Up |
|
||||||
|
|mod + `KC_N` ↔ mod + `KC_P` | Next ↔ Previous |
|
||||||
|
|mod + `KC_A` ↔ mod + `KC_E` | Home ↔ End |
|
||||||
|
|mod + `KC_O` ↔ mod + `KC_I` | Vim jump list Older ↔ Newer |
|
||||||
|
|`KC_J` ↔ `KC_K` | Down ↔ Up |
|
||||||
|
|`KC_H` ↔ `KC_L` | Left ↔ Right |
|
||||||
|
|`KC_W` ↔ `KC_B` | Forward ↔ Backward by Word |
|
||||||
|
|
||||||
|
(where above, "mod" is Ctrl, Alt, or GUI)
|
||||||
|
|
||||||
|
|
||||||
|
## Defining alternate keys
|
||||||
|
|
||||||
|
Use the `get_alt_repeat_key_keycode_user()` callback to define the "alternate"
|
||||||
|
for additional keys or override the default definitions. For example, to define
|
||||||
|
Ctrl + Y as the alternate of Ctrl + Z, and vice versa, add the following in
|
||||||
|
keymap.c:
|
||||||
|
|
||||||
|
```c
|
||||||
|
uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) {
|
||||||
|
if ((mods & MOD_MASK_CTRL)) { // Was Ctrl held?
|
||||||
|
switch (keycode) {
|
||||||
|
case KC_Y: return C(KC_Z); // Ctrl + Y reverses to Ctrl + Z.
|
||||||
|
case KC_Z: return C(KC_Y); // Ctrl + Z reverses to Ctrl + Y.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return KC_TRNS; // Defer to default definitions.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `keycode` and `mods` args are the keycode and mods that were active with the
|
||||||
|
last pressed key. The meaning of the return value from this function is:
|
||||||
|
|
||||||
|
* `KC_NO` – do nothing (any predefined alternate key is not used);
|
||||||
|
* `KC_TRNS` – use the default alternate key if it exists;
|
||||||
|
* anything else – use the specified keycode. Any keycode may be returned
|
||||||
|
as an alternate key, including custom keycodes.
|
||||||
|
|
||||||
|
Another example, defining Shift + Tab as the alternate of Tab, and vice versa:
|
||||||
|
|
||||||
|
```c
|
||||||
|
uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) {
|
||||||
|
bool shifted = (mods & MOD_MASK_SHIFT); // Was Shift held?
|
||||||
|
switch (keycode) {
|
||||||
|
case KC_TAB:
|
||||||
|
if (shifted) { // If the last key was Shift + Tab,
|
||||||
|
return KC_TAB; // ... the reverse is Tab.
|
||||||
|
} else { // Otherwise, the last key was Tab,
|
||||||
|
return S(KC_TAB); // ... and the reverse is Shift + Tab.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return KC_TRNS;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Eliminating SFBs
|
||||||
|
|
||||||
|
Alternate Repeat can be configured more generally to perform an action that
|
||||||
|
"complements" the last key. Alternate Repeat is not limited to reverse
|
||||||
|
repeating, and it need not be symmetric. You can use it to eliminate cases of
|
||||||
|
same-finger bigrams in your layout, that is, pairs of letters typed by the same
|
||||||
|
finger. The following addresses the top 5 same-finger bigrams in English on
|
||||||
|
QWERTY, so that for instance "`ed`" may be typed as <kbd>E</kbd>, <kbd>Alt
|
||||||
|
Repeat</kbd>.
|
||||||
|
|
||||||
|
```c
|
||||||
|
uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) {
|
||||||
|
switch (keycode) {
|
||||||
|
case KC_E: return KC_D; // For "ED" bigram.
|
||||||
|
case KC_D: return KC_E; // For "DE" bigram.
|
||||||
|
case KC_C: return KC_E; // For "CE" bigram.
|
||||||
|
case KC_L: return KC_O; // For "LO" bigram.
|
||||||
|
case KC_U: return KC_N; // For "UN" bigram.
|
||||||
|
}
|
||||||
|
|
||||||
|
return KC_TRNS;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Typing shortcuts
|
||||||
|
|
||||||
|
A useful possibility is having Alternate Repeat press [a
|
||||||
|
macro](feature_macros.md). This way macros can be used without having to
|
||||||
|
dedicate keys to them. The following defines a couple shortcuts.
|
||||||
|
|
||||||
|
* Typing <kbd>K</kbd>, <kbd>Alt Repeat</kbd> produces "`keyboard`," with the
|
||||||
|
initial "`k`" typed as usual and the "`eybord`" produced by the macro.
|
||||||
|
* Typing <kbd>.</kbd>, <kbd>Alt Repeat</kbd> produces "`../`," handy for "up
|
||||||
|
directory" on the shell. Similary, <kbd>.</kbd> types the initial "`.`" and
|
||||||
|
"`./`" is produced by the macro.
|
||||||
|
|
||||||
|
```c
|
||||||
|
enum custom_keycodes {
|
||||||
|
M_KEYBOARD = SAFE_RANGE,
|
||||||
|
M_UPDIR,
|
||||||
|
// Other custom keys...
|
||||||
|
};
|
||||||
|
|
||||||
|
uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) {
|
||||||
|
switch (keycode) {
|
||||||
|
case KC_K: return M_KEYBOARD;
|
||||||
|
case KC_DOT: return M_UPDIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return KC_TRNS;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool process_record_user(uint16_t keycode, keyrecord_t* record) {
|
||||||
|
switch (keycode) {
|
||||||
|
case M_KEYBOARD: SEND_STRING(/*k*/"eyboard"); break;
|
||||||
|
case M_UPDIR: SEND_STRING(/*.*/"./"); break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ignoring certain keys and mods
|
||||||
|
|
||||||
|
In tracking what is "the last key" to be repeated or alternate repeated,
|
||||||
|
modifier and layer switch keys are always ignored. This makes it possible to set
|
||||||
|
some mods and change layers between pressing a key and repeating it. By default,
|
||||||
|
all other (non-modifier, non-layer switch) keys are remembered so that they are
|
||||||
|
eligible for repeating. To configure additional keys to be ignored, define
|
||||||
|
`remember_last_key_user()` in your keymap.c.
|
||||||
|
|
||||||
|
#### Ignoring a key
|
||||||
|
|
||||||
|
The following ignores the Backspace key:
|
||||||
|
|
||||||
|
```c
|
||||||
|
bool remember_last_key_user(uint16_t keycode, keyrecord_t* record,
|
||||||
|
uint8_t* remembered_mods) {
|
||||||
|
switch (keycode) {
|
||||||
|
case KC_BSPC:
|
||||||
|
return false; // Ignore backspace.
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // Other keys can be repeated.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then for instance, the Repeat key in <kbd>Left Arrow</kbd>,
|
||||||
|
<kbd>Backspace</kbd>, <kbd>Repeat</kbd> sends Left Arrow again instead of
|
||||||
|
repeating Backspace.
|
||||||
|
|
||||||
|
The `remember_last_key_user()` callback is called on every key press excluding
|
||||||
|
modifiers and layer switches. Returning true indicates the key is remembered,
|
||||||
|
while false means it is ignored.
|
||||||
|
|
||||||
|
#### Filtering remembered mods
|
||||||
|
|
||||||
|
The `remembered_mods` arg represents the mods that will be remembered with
|
||||||
|
this key. It can be modified to forget certain mods. This may be
|
||||||
|
useful to forget capitalization when repeating shifted letters, so that "Aaron"
|
||||||
|
does not becom "AAron":
|
||||||
|
|
||||||
|
```c
|
||||||
|
bool remember_last_key_user(uint16_t keycode, keyrecord_t* record,
|
||||||
|
uint8_t* remembered_mods) {
|
||||||
|
// Forget Shift on letter keys when Shift or AltGr are the only mods.
|
||||||
|
switch (keycode) {
|
||||||
|
case KC_A ... KC_Z:
|
||||||
|
if ((*remembered_mods & ~(MOD_MASK_SHIFT | MOD_BIT(KC_RALT))) == 0) {
|
||||||
|
*remembered_mods &= ~MOD_MASK_SHIFT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Further conditions
|
||||||
|
|
||||||
|
Besides checking the keycode, this callback could also make conditions based on
|
||||||
|
the current layer state (with `IS_LAYER_ON(layer)`) or mods (`get_mods()`). For
|
||||||
|
example, the following ignores keys on layer 2 as well as key combinations
|
||||||
|
involving GUI:
|
||||||
|
|
||||||
|
```c
|
||||||
|
bool remember_last_key_user(uint16_t keycode, keyrecord_t* record,
|
||||||
|
uint8_t* remembered_mods) {
|
||||||
|
if (IS_LAYER_ON(2) || (get_mods() & MOD_MASK_GUI)) {
|
||||||
|
return false; // Ignore layer 2 keys and GUI chords.
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // Other keys can be repeated.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
?> See [Layer Functions](feature_layers.md#functions) and [Checking Modifier
|
||||||
|
State](feature_advanced_keycodes.md#checking-modifier-state) for further
|
||||||
|
details.
|
||||||
|
|
||||||
|
|
||||||
|
## Handle how a key is repeated
|
||||||
|
|
||||||
|
By default, pressing the Repeat Key will simply behave as if the last key
|
||||||
|
were pressed again. This also works with macro keys with custom handlers,
|
||||||
|
invoking the macro again. In case fine-tuning is needed for sensible repetition,
|
||||||
|
you can handle how a key is repeated with `get_repeat_key_count()` within
|
||||||
|
`process_record_user()`.
|
||||||
|
|
||||||
|
The `get_repeat_key_count()` function returns a signed count of times the key
|
||||||
|
has been repeated or alternate repeated. When a key is pressed as usual,
|
||||||
|
`get_repeat_key_count()` is 0. On the first repeat, it is 1, then the second
|
||||||
|
repeat, 2, and so on. Negative counts are used similarly for alternate
|
||||||
|
repeating. For instance supposing `MY_MACRO` is a custom keycode used in the
|
||||||
|
layout:
|
||||||
|
|
||||||
|
```c
|
||||||
|
bool process_record_user(uint16_t keycode, keyrecord_t* record) {
|
||||||
|
switch (keycode) {
|
||||||
|
case MY_MACRO:
|
||||||
|
if (get_repeat_key_count() > 0) {
|
||||||
|
// MY_MACRO is being repeated!
|
||||||
|
if (record->event.pressed) {
|
||||||
|
SEND_STRING("repeat!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// MY_MACRO is being used normally.
|
||||||
|
if (record->event.pressed) {
|
||||||
|
SEND_STRING("macro");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Other macros...
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Handle how a key is alternate repeated
|
||||||
|
|
||||||
|
Pressing the Alternate Repeat Key behaves as if the "alternate" of the last
|
||||||
|
pressed key were pressed, if an alternate is defined. To define how a particular
|
||||||
|
key is alternate repeated, use the `get_alt_repeat_key_keycode_user()` callback
|
||||||
|
as described above to define which keycode to use as its alternate. Beyond this,
|
||||||
|
`get_repeat_key_count()` may be used in custom handlers to fine-tune behavior
|
||||||
|
when alternate repeating.
|
||||||
|
|
||||||
|
The following example defines `MY_MACRO` as its own alternate, and specially
|
||||||
|
handles repeating and alternate repeating:
|
||||||
|
|
||||||
|
```c
|
||||||
|
uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) {
|
||||||
|
switch (keycode) {
|
||||||
|
case MY_MACRO: return MY_MACRO; // MY_MACRO is its own alternate.
|
||||||
|
}
|
||||||
|
return KC_TRNS;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool process_record_user(uint16_t keycode, keyrecord_t* record) {
|
||||||
|
switch (keycode) {
|
||||||
|
case MY_MACRO:
|
||||||
|
if (get_repeat_key_count() > 0) { // Repeating.
|
||||||
|
if (record->event.pressed) {
|
||||||
|
SEND_STRING("repeat!");
|
||||||
|
}
|
||||||
|
} else if (get_repeat_key_count() < 0) { // Alternate repeating.
|
||||||
|
if (record->event.pressed) {
|
||||||
|
SEND_STRING("alt repeat!");
|
||||||
|
}
|
||||||
|
} else { // Used normally.
|
||||||
|
if (record->event.pressed) {
|
||||||
|
SEND_STRING("macro");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Other macros...
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|--------------------------------|------------------------------------------------------------------------|
|
||||||
|
| `get_last_keycode()` | The last key's keycode, the key to be repeated. |
|
||||||
|
| `get_last_mods()` | Mods to apply when repeating. |
|
||||||
|
| `set_last_keycode(kc)` | Set the keycode to be repeated. |
|
||||||
|
| `set_last_mods(mods)` | Set the mods to apply when repeating. |
|
||||||
|
| `get_repeat_key_count()` | Signed count of times the key has been repeated or alternate repeated. |
|
||||||
|
| `get_alt_repeat_key_keycode()` | Keycode to be used for alternate repeating. |
|
||||||
|
|
||||||
|
|
||||||
|
## Additional "Alternate" keys
|
||||||
|
|
||||||
|
By leveraging `get_last_keycode()` in macros, it is possible to define
|
||||||
|
additional, distinct "Alternate Repeat"-like keys. The following defines two
|
||||||
|
keys `ALTREP2` and `ALTREP3` and implements ten shortcuts with them for common
|
||||||
|
English 5-gram letter patterns, taking inspiration from
|
||||||
|
[Stenotype](feature_stenography.md):
|
||||||
|
|
||||||
|
|
||||||
|
| Typing | Produces | Typing | Produces |
|
||||||
|
|----------------------------------|----------|----------------------------------|----------|
|
||||||
|
| <kbd>A</kbd>, <kbd>ALTREP2</kbd> | `ation` | <kbd>A</kbd>, <kbd>ALTREP3</kbd> | `about` |
|
||||||
|
| <kbd>I</kbd>, <kbd>ALTREP2</kbd> | `ition` | <kbd>I</kbd>, <kbd>ALTREP3</kbd> | `inter` |
|
||||||
|
| <kbd>S</kbd>, <kbd>ALTREP2</kbd> | `ssion` | <kbd>S</kbd>, <kbd>ALTREP3</kbd> | `state` |
|
||||||
|
| <kbd>T</kbd>, <kbd>ALTREP2</kbd> | `their` | <kbd>T</kbd>, <kbd>ALTREP3</kbd> | `there` |
|
||||||
|
| <kbd>W</kbd>, <kbd>ALTREP2</kbd> | `which` | <kbd>W</kbd>, <kbd>ALTREP3</kbd> | `would` |
|
||||||
|
|
||||||
|
```c
|
||||||
|
enum custom_keycodes {
|
||||||
|
ALTREP2 = SAFE_RANGE,
|
||||||
|
ALTREP3,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use ALTREP2 and ALTREP3 in your layout...
|
||||||
|
|
||||||
|
bool remember_last_key_user(uint16_t keycode, keyrecord_t* record,
|
||||||
|
uint8_t* remembered_mods) {
|
||||||
|
switch (keycode) {
|
||||||
|
case ALTREP2:
|
||||||
|
case ALTREP3:
|
||||||
|
return false; // Ignore ALTREP keys.
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // Other keys can be repeated.
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_altrep2(uint16_t keycode, uint8_t mods) {
|
||||||
|
switch (keycode) {
|
||||||
|
case KC_A: SEND_STRING(/*a*/"tion"); break;
|
||||||
|
case KC_I: SEND_STRING(/*i*/"tion"); break;
|
||||||
|
case KC_S: SEND_STRING(/*s*/"sion"); break;
|
||||||
|
case KC_T: SEND_STRING(/*t*/"heir"); break;
|
||||||
|
case KC_W: SEND_STRING(/*w*/"hich"); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_altrep3(uint16_t keycode, uint8_t mods) {
|
||||||
|
switch (keycode) {
|
||||||
|
case KC_A: SEND_STRING(/*a*/"bout"); break;
|
||||||
|
case KC_I: SEND_STRING(/*i*/"nter"); break;
|
||||||
|
case KC_S: SEND_STRING(/*s*/"tate"); break;
|
||||||
|
case KC_T: SEND_STRING(/*t*/"here"); break;
|
||||||
|
case KC_W: SEND_STRING(/*w*/"ould"); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool process_record_user(uint16_t keycode, keyrecord_t* record) {
|
||||||
|
switch (keycode) {
|
||||||
|
case ALTREP2:
|
||||||
|
if (record->event.pressed) {
|
||||||
|
process_altrep2(get_last_keycode(), get_last_mods());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case ALTREP3:
|
||||||
|
if (record->event.pressed) {
|
||||||
|
process_altrep3(get_last_keycode(), get_last_mods());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
@ -156,6 +156,82 @@ const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT] = {
|
|||||||
Where `X_Y` is the location of the LED in the matrix defined by [the datasheet](https://www.issi.com/WW/pdf/31FL3733.pdf) and the header file `drivers/led/issi/is31fl3733.h`. The `driver` is the index of the driver you defined in your `config.h` (`0`, `1`, `2`, or `3` for now).
|
Where `X_Y` is the location of the LED in the matrix defined by [the datasheet](https://www.issi.com/WW/pdf/31FL3733.pdf) and the header file `drivers/led/issi/is31fl3733.h`. The `driver` is the index of the driver you defined in your `config.h` (`0`, `1`, `2`, or `3` for now).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
### IS31FL3736 :id=is31fl3736
|
||||||
|
|
||||||
|
There is basic support for addressable RGB matrix lighting with the I2C IS31FL3736 RGB controller. To enable it, add this to your `rules.mk`:
|
||||||
|
|
||||||
|
```make
|
||||||
|
RGB_MATRIX_ENABLE = yes
|
||||||
|
RGB_MATRIX_DRIVER = IS31FL3736
|
||||||
|
```
|
||||||
|
You can use between 1 and 4 IS31FL3736 IC's. Do not specify `DRIVER_ADDR_<N>` defines for IC's that are not present on your keyboard.
|
||||||
|
|
||||||
|
Configure the hardware via your `config.h`:
|
||||||
|
|
||||||
|
| Variable | Description | Default |
|
||||||
|
|----------|-------------|---------|
|
||||||
|
| `ISSI_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 |
|
||||||
|
| `ISSI_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 |
|
||||||
|
| `ISSI_PWM_FREQUENCY` | (Optional) PWM Frequency Setting - IS31FL3736B only | 0 |
|
||||||
|
| `ISSI_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF |
|
||||||
|
| `ISSI_SWPULLUP` | (Optional) Set the value of the SWx lines on-chip de-ghosting resistors | PUR_0R (Disabled) |
|
||||||
|
| `ISSI_CSPULLUP` | (Optional) Set the value of the CSx lines on-chip de-ghosting resistors | PUR_0R (Disabled) |
|
||||||
|
| `DRIVER_COUNT` | (Required) How many RGB driver IC's are present | |
|
||||||
|
| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | |
|
||||||
|
| `DRIVER_ADDR_1` | (Required) Address for the first RGB driver | |
|
||||||
|
| `DRIVER_ADDR_2` | (Optional) Address for the second RGB driver | |
|
||||||
|
| `DRIVER_ADDR_3` | (Optional) Address for the third RGB driver | |
|
||||||
|
| `DRIVER_ADDR_4` | (Optional) Address for the fourth RGB driver | |
|
||||||
|
|
||||||
|
The IS31FL3736 IC's have on-chip resistors that can be enabled to allow for de-ghosting of the RGB matrix. By default these resistors are not enabled (`ISSI_SWPULLUP`/`ISSI_CSPULLUP` are given the value of`PUR_0R`), the values that can be set to enable de-ghosting are as follows:
|
||||||
|
|
||||||
|
| `ISSI_SWPULLUP/ISSI_CSPULLUP` | Description |
|
||||||
|
|----------------------|-------------|
|
||||||
|
| `PUR_0R` | (default) Do not use the on-chip resistors/enable de-ghosting |
|
||||||
|
| `PUR_05KR` | The 0.5k Ohm resistor used during blanking period (t_NOL) |
|
||||||
|
| `PUR_1KR` | The 1k Ohm resistor used during blanking period (t_NOL) |
|
||||||
|
| `PUR_2KR` | The 2k Ohm resistor used during blanking period (t_NOL) |
|
||||||
|
| `PUR_4KR` | The 4k Ohm resistor used during blanking period (t_NOL) |
|
||||||
|
| `PUR_8KR` | The 8k Ohm resistor during blanking period (t_NOL) |
|
||||||
|
| `PUR_16KR` | The 16k Ohm resistor during blanking period (t_NOL) |
|
||||||
|
| `PUR_32KR` | The 32k Ohm resistor used during blanking period (t_NOL) |
|
||||||
|
|
||||||
|
Here is an example using 2 drivers.
|
||||||
|
|
||||||
|
```c
|
||||||
|
// This is a 7-bit address, that gets left-shifted and bit 0
|
||||||
|
// set to 0 for write, 1 for read (as per I2C protocol)
|
||||||
|
// The address will vary depending on your wiring:
|
||||||
|
// 0000 <-> GND
|
||||||
|
// 0101 <-> SCL
|
||||||
|
// 1010 <-> SDA
|
||||||
|
// 1111 <-> VCC
|
||||||
|
// ADDR represents A3:A0 of the 7-bit address.
|
||||||
|
// The result is: 0b101(ADDR)
|
||||||
|
#define DRIVER_ADDR_1 0b1010000
|
||||||
|
#define DRIVER_ADDR_2 0b1010001
|
||||||
|
|
||||||
|
#define DRIVER_COUNT 2
|
||||||
|
#define DRIVER_1_LED_TOTAL 30
|
||||||
|
#define DRIVER_2_LED_TOTAL 32
|
||||||
|
#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
|
||||||
|
```
|
||||||
|
!> Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`.
|
||||||
|
|
||||||
|
Define these arrays listing all the LEDs in your `<keyboard>.c`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT] = {
|
||||||
|
/* Refer to IS31 manual for these locations
|
||||||
|
* driver
|
||||||
|
* | R location
|
||||||
|
* | | G location
|
||||||
|
* | | | B location
|
||||||
|
* | | | | */
|
||||||
|
{0, B_1, A_1, C_1},
|
||||||
|
....
|
||||||
|
}
|
||||||
|
```
|
||||||
### IS31FL3737 :id=is31fl3737
|
### IS31FL3737 :id=is31fl3737
|
||||||
|
|
||||||
There is basic support for addressable RGB matrix lighting with the I2C IS31FL3737 RGB controller. To enable it, add this to your `rules.mk`:
|
There is basic support for addressable RGB matrix lighting with the I2C IS31FL3737 RGB controller. To enable it, add this to your `rules.mk`:
|
||||||
@ -218,8 +294,6 @@ Here is an example using 2 drivers.
|
|||||||
```
|
```
|
||||||
!> Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`.
|
!> Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`.
|
||||||
|
|
||||||
Currently only 2 drivers are supported, but it would be trivial to support all 4 combinations.
|
|
||||||
|
|
||||||
Define these arrays listing all the LEDs in your `<keyboard>.c`:
|
Define these arrays listing all the LEDs in your `<keyboard>.c`:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
@ -361,7 +435,7 @@ Configure the hardware via your `config.h`:
|
|||||||
|
|
||||||
```c
|
```c
|
||||||
// The pin connected to the data pin of the LEDs
|
// The pin connected to the data pin of the LEDs
|
||||||
#define RGB_DI_PIN D7
|
#define WS2812_DI_PIN D7
|
||||||
// The number of LEDs connected
|
// The number of LEDs connected
|
||||||
#define RGB_MATRIX_LED_COUNT 70
|
#define RGB_MATRIX_LED_COUNT 70
|
||||||
```
|
```
|
||||||
@ -383,9 +457,9 @@ Configure the hardware via your `config.h`:
|
|||||||
|
|
||||||
```c
|
```c
|
||||||
// The pin connected to the data pin of the LEDs
|
// The pin connected to the data pin of the LEDs
|
||||||
#define RGB_DI_PIN D7
|
#define APA102_DI_PIN D7
|
||||||
// The pin connected to the clock pin of the LEDs
|
// The pin connected to the clock pin of the LEDs
|
||||||
#define RGB_CI_PIN D6
|
#define APA102_CI_PIN D6
|
||||||
// The number of LEDs connected
|
// The number of LEDs connected
|
||||||
#define RGB_MATRIX_LED_COUNT 70
|
#define RGB_MATRIX_LED_COUNT 70
|
||||||
```
|
```
|
||||||
@ -690,6 +764,14 @@ Remove the spread effect entirely.
|
|||||||
#define RGB_MATRIX_TYPING_HEATMAP_SLIM
|
#define RGB_MATRIX_TYPING_HEATMAP_SLIM
|
||||||
```
|
```
|
||||||
|
|
||||||
|
It's also possible to adjust the tempo of *heating up*. It's defined as the number of shades that are
|
||||||
|
increased on the [HSV scale](https://en.wikipedia.org/wiki/HSL_and_HSV). Decreasing this value increases
|
||||||
|
the number of keystrokes needed to fully heat up the key.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define RGB_MATRIX_TYPING_HEATMAP_INCREASE_STEP 32
|
||||||
|
```
|
||||||
|
|
||||||
### RGB Matrix Effect Solid Reactive :id=rgb-matrix-effect-solid-reactive
|
### RGB Matrix Effect Solid Reactive :id=rgb-matrix-effect-solid-reactive
|
||||||
|
|
||||||
Solid reactive effects will pulse RGB light on key presses with user configurable hues. To enable gradient mode that will automatically change reactive color, add the following define:
|
Solid reactive effects will pulse RGB light on key presses with user configurable hues. To enable gradient mode that will automatically change reactive color, add the following define:
|
||||||
@ -811,13 +893,7 @@ These are defined in [`color.h`](https://github.com/qmk/qmk_firmware/blob/master
|
|||||||
|
|
||||||
## EEPROM storage :id=eeprom-storage
|
## EEPROM storage :id=eeprom-storage
|
||||||
|
|
||||||
The EEPROM for it is currently shared with the LED Matrix system (it's generally assumed only one feature would be used at a time), but could be configured to use its own 32bit address with:
|
The EEPROM for it is currently shared with the LED Matrix system (it's generally assumed only one feature would be used at a time).
|
||||||
|
|
||||||
```c
|
|
||||||
#define EECONFIG_RGB_MATRIX (uint32_t *)28
|
|
||||||
```
|
|
||||||
|
|
||||||
Where `28` is an unused index from `eeconfig.h`.
|
|
||||||
|
|
||||||
## Functions :id=functions
|
## Functions :id=functions
|
||||||
|
|
||||||
|
@ -33,12 +33,13 @@ RGBLIGHT_DRIVER = APA102
|
|||||||
|
|
||||||
At minimum you must define the data pin your LED strip is connected to, and the number of LEDs in the strip, in your `config.h`. For APA102 LEDs, you must also define the clock pin. If your keyboard has onboard RGB LEDs, and you are simply creating a keymap, you usually won't need to modify these.
|
At minimum you must define the data pin your LED strip is connected to, and the number of LEDs in the strip, in your `config.h`. For APA102 LEDs, you must also define the clock pin. If your keyboard has onboard RGB LEDs, and you are simply creating a keymap, you usually won't need to modify these.
|
||||||
|
|
||||||
|Define |Description |
|
|Define |Description |
|
||||||
|---------------|---------------------------------------------------------------------------------------------------------|
|
|---------------|-------------------------------------------------------------------------|
|
||||||
|`RGB_DI_PIN` |The pin connected to the data pin of the LEDs |
|
|`WS2812_DI_PIN`|The pin connected to the data pin of the LEDs (WS2812) |
|
||||||
|`RGB_CI_PIN` |The pin connected to the clock pin of the LEDs (APA102 only) |
|
|`APA102_DI_PIN`|The pin connected to the data pin of the LEDs (APA102) |
|
||||||
|`RGBLED_NUM` |The number of LEDs connected |
|
|`APA102_CI_PIN`|The pin connected to the clock pin of the LEDs (APA102) |
|
||||||
|`RGBLED_SPLIT` |(Optional) For split keyboards, the number of LEDs connected on each half directly wired to `RGB_DI_PIN` |
|
|`RGBLED_NUM` |The number of LEDs connected |
|
||||||
|
|`RGBLED_SPLIT` |(Optional) For split keyboards, the number of LEDs connected on each half|
|
||||||
|
|
||||||
Then you should be able to use the keycodes below to change the RGB lighting to your liking.
|
Then you should be able to use the keycodes below to change the RGB lighting to your liking.
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Space Cadet: The Future, Built In
|
# Space Cadet: The Future, Built In
|
||||||
|
|
||||||
Steve Losh described the [Space Cadet Shift](https://stevelosh.com/blog/2012/10/a-modern-space-cadet/) quite well. Essentially, when you tap Left Shift on its own, you get an opening parenthesis; tap Right Shift on its own and you get the closing one. When held, the Shift keys function as normal. Yes, it's as cool as it sounds, and now even cooler supporting Control and Alt as well!
|
Steve Losh described the [Space Cadet Shift](https://web.archive.org/web/20230330090938/https://stevelosh.com/blog/2012/10/a-modern-space-cadet/) quite well. Essentially, when you tap Left Shift on its own, you get an opening parenthesis; tap Right Shift on its own and you get the closing one. When held, the Shift keys function as normal. Yes, it's as cool as it sounds, and now even cooler supporting Control and Alt as well!
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -300,6 +300,12 @@ This enables transmitting the pointing device status to the master side of the s
|
|||||||
|
|
||||||
This enables triggering of haptic feedback on the slave side of the split keyboard. For DRV2605L this will send the mode, but for solenoids it is expected that the desired mode is already set up on the slave.
|
This enables triggering of haptic feedback on the slave side of the split keyboard. For DRV2605L this will send the mode, but for solenoids it is expected that the desired mode is already set up on the slave.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define SPLIT_ACTIVITY_ENABLE
|
||||||
|
```
|
||||||
|
|
||||||
|
This synchronizes the activity timestamps between sides of the split keyboard, allowing for activity timeouts to occur.
|
||||||
|
|
||||||
### Custom data sync between sides :id=custom-data-sync
|
### Custom data sync between sides :id=custom-data-sync
|
||||||
|
|
||||||
QMK's split transport allows for arbitrary data transactions at both the keyboard and user levels. This is modelled on a remote procedure call, with the master invoking a function on the slave side, with the ability to send data from master to slave, process it slave side, and send data back from slave to master.
|
QMK's split transport allows for arbitrary data transactions at both the keyboard and user levels. This is modelled on a remote procedure call, with the master invoking a function on the slave side, with the ability to send data from master to slave, process it slave side, and send data back from slave to master.
|
||||||
|
@ -138,7 +138,7 @@ bool post_process_steno_user(uint16_t keycode, keyrecord_t *record, steno_mode_t
|
|||||||
|
|
||||||
This function is called after a key has been processed, but before any decision about whether or not to send a chord. This is where to put hooks for things like, say, live displays of steno chords or keys.
|
This function is called after a key has been processed, but before any decision about whether or not to send a chord. This is where to put hooks for things like, say, live displays of steno chords or keys.
|
||||||
|
|
||||||
If `IS_PRESSED(record->event)` is false, and `n_pressed_keys` is 0 or 1, the chord will be sent shortly, but has not yet been sent. This relieves you of the need of keeping track of where a packet ends and another begins.
|
If `record->event.pressed` is false, and `n_pressed_keys` is 0 or 1, the chord will be sent shortly, but has not yet been sent. This relieves you of the need of keeping track of where a packet ends and another begins.
|
||||||
|
|
||||||
The `chord` argument contains the packet of the current chord as specified by the protocol in use. This is *NOT* simply a list of chorded steno keys of the form `[STN_E, STN_U, STN_BR, STN_GR]`. Refer to the appropriate protocol section of this document to learn more about the format of the packets in your steno protocol/mode of choice.
|
The `chord` argument contains the packet of the current chord as specified by the protocol in use. This is *NOT* simply a list of chorded steno keys of the form `[STN_E, STN_U, STN_BR, STN_GR]`. Refer to the appropriate protocol section of this document to learn more about the format of the packets in your steno protocol/mode of choice.
|
||||||
|
|
||||||
|
@ -47,6 +47,11 @@ const uint8_t PROGMEM encoder_hand_swap_config[NUM_ENCODERS] = { 1, 0 };
|
|||||||
|
|
||||||
### Functions :id=functions
|
### Functions :id=functions
|
||||||
|
|
||||||
| Function | Description |
|
User callback functions to manipulate Swap-Hands:
|
||||||
|----------------------|---------------------------------------------|
|
|
||||||
| `is_swap_hands_on()` | Returns true if Swap-Hands is currently on. |
|
| Function | Description |
|
||||||
|
|-----------------------|---------------------------------------------|
|
||||||
|
| `swap_hands_on()` | Turns Swap-Hands on. |
|
||||||
|
| `swap_hands_off()` | Turns Swap-Hands off. |
|
||||||
|
| `swap_hands_toggle()` | Toggles Swap-Hands. |
|
||||||
|
| `is_swap_hands_on()` | Returns true if Swap-Hands is currently on. |
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Word Per Minute (WPM) Calculcation
|
# Word Per Minute (WPM) Calculation
|
||||||
|
|
||||||
The WPM feature uses time between keystrokes to compute a rolling average words per minute rate and makes this available for various uses.
|
The WPM feature uses time between keystrokes to compute a rolling average words per minute rate and makes this available for various uses.
|
||||||
|
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
# Vagrant Quick Start
|
|
||||||
|
|
||||||
This project includes a `Vagrantfile` that will allow you to build a new firmware for your keyboard very easily without major changes to your primary operating system. This also ensures that when you clone the project and perform a build, you have the exact same environment as anyone else using the Vagrantfile to build. This makes it much easier for people to help you troubleshoot any issues you encounter.
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
Using the `Vagrantfile` in this repository requires you have [Vagrant](https://www.vagrantup.com/) as well as a supported provider installed:
|
|
||||||
|
|
||||||
* [VirtualBox](https://www.virtualbox.org/) (Version at least 5.0.12)
|
|
||||||
* Sold as 'the most accessible platform to use Vagrant'
|
|
||||||
* [VMware Workstation](https://www.vmware.com/products/workstation) and [Vagrant VMware plugin](https://www.vagrantup.com/vmware)
|
|
||||||
* The (paid) VMware plugin requires a licensed copy of VMware Workstation/Fusion
|
|
||||||
* [Docker](https://www.docker.com/)
|
|
||||||
|
|
||||||
Other than having Vagrant, a suitable provider installed and possibly a restart of your computer afterwards, you can simple run a 'vagrant up' anywhere inside the folder where you checked out this project and it will start an environment (either a virtual machine or container) that contains all the tools required to build this project. There is a post Vagrant startup hint that will get you off on the right foot, otherwise you can also reference the build documentation below.
|
|
||||||
|
|
||||||
## Flashing the Firmware
|
|
||||||
|
|
||||||
The "easy" way to flash the firmware is using a tool from your host OS:
|
|
||||||
|
|
||||||
* [QMK Toolbox](https://github.com/qmk/qmk_toolbox) (recommended)
|
|
||||||
* [Teensy Loader](https://www.pjrc.com/teensy/loader.html)
|
|
||||||
|
|
||||||
If you want to program via the command line you can uncomment the ['modifyvm'] lines in the Vagrantfile to enable the USB passthrough into Linux and then program using the command line tools like dfu-util/dfu-programmer or you can install the Teensy CLI version.
|
|
||||||
|
|
||||||
## Vagrantfile Overview
|
|
||||||
The development environment is configured to run the QMK Docker image, `qmkfm/qmk_cli`. This not only ensures predictability between systems, it also mirrors the CI environment.
|
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
### Why am I seeing issues under Virtualbox?
|
|
||||||
Certain versions of Virtualbox 5 appear to have an incompatibility with the Virtualbox extensions installed in the boxes in this Vagrantfile. If you encounter any issues with the /vagrant mount not succeeding, please upgrade your version of Virtualbox to at least 5.0.12. **Alternately, you can try running the following command:**
|
|
||||||
|
|
||||||
```
|
|
||||||
vagrant plugin install vagrant-vbguest
|
|
||||||
```
|
|
||||||
|
|
||||||
### How do I remove an existing environment?
|
|
||||||
Finished with your environment? From anywhere inside the folder where you checked out this project, Execute:
|
|
||||||
|
|
||||||
```
|
|
||||||
vagrant destroy
|
|
||||||
```
|
|
||||||
|
|
||||||
### What if I want to use Docker directly?
|
|
||||||
Want to benefit from the Vagrant workflow without a virtual machine? The Vagrantfile is configured to bypass running a virtual machine, and run the container directly. Execute the following when bringing up the environment to force the use of Docker:
|
|
||||||
```
|
|
||||||
vagrant up --provider=docker
|
|
||||||
```
|
|
||||||
|
|
||||||
### How do I access the virtual machine instead of the Docker container?
|
|
||||||
Execute the following to bypass the `vagrant` user booting directly to the official qmk builder image:
|
|
||||||
|
|
||||||
```
|
|
||||||
vagrant ssh -c 'sudo -i'
|
|
||||||
```
|
|
@ -35,6 +35,40 @@ To use a 5V/16MHz Pro Micro as an ISP flashing tool, you will first need to load
|
|||||||
|
|
||||||
!> Note that the `10` pin on the Pro Micro should be wired to the `RESET` pin on the keyboard's controller. ***DO NOT*** connect the `RESET` pin on the Pro Micro to the `RESET` on the keyboard.
|
!> Note that the `10` pin on the Pro Micro should be wired to the `RESET` pin on the keyboard's controller. ***DO NOT*** connect the `RESET` pin on the Pro Micro to the `RESET` on the keyboard.
|
||||||
|
|
||||||
|
|
||||||
|
### Arduino Uno / Micro as ISP
|
||||||
|
|
||||||
|
[Arduino Uno](https://store.arduino.cc/products/arduino-uno-rev3)
|
||||||
|
[Arduino Micro](https://store.arduino.cc/products/arduino-micro)
|
||||||
|
|
||||||
|
A standard Uno or Micro can be used as an ISP flashing tool using the [example "ArduinoISP" sketch](https://docs.arduino.cc/built-in-examples/arduino-isp/ArduinoISP#load-the-sketch) to emulate an STK500 ISP. Also works with Sparkfun Pro Micros and clones.
|
||||||
|
|
||||||
|
**AVRDUDE Programmer**: `stk500v1`
|
||||||
|
**AVRDUDE Port**: Serial
|
||||||
|
|
||||||
|
#### Wiring
|
||||||
|
|
||||||
|
|Uno |Keyboard|
|
||||||
|
|-----------|--------|
|
||||||
|
|`5V` |`VCC` |
|
||||||
|
|`GND` |`GND` |
|
||||||
|
|`10` (`B2`)|`RESET` |
|
||||||
|
|`13` (`B5`)|`SCLK` |
|
||||||
|
|`11` (`B3`)|`MOSI` |
|
||||||
|
|`12` (`B4`)|`MISO` |
|
||||||
|
|
||||||
|
|Micro |Keyboard|
|
||||||
|
|-----------|--------|
|
||||||
|
|`5V` |`VCC` |
|
||||||
|
|`GND` |`GND` |
|
||||||
|
|`10` (`B6`)|`RESET` |
|
||||||
|
|`15` (`B1`)|`SCLK` |
|
||||||
|
|`16` (`B2`)|`MOSI` |
|
||||||
|
|`14` (`B3`)|`MISO` |
|
||||||
|
|
||||||
|
!> Note that the `10` pin on the Uno/Micro should be wired to the `RESET` pin on the keyboard's controller. ***DO NOT*** connect the `RESET` pin on the Uno/Micro to the `RESET` on the keyboard.
|
||||||
|
|
||||||
|
|
||||||
### Teensy 2.0 as ISP
|
### Teensy 2.0 as ISP
|
||||||
|
|
||||||
[PJRC Teensy 2.0](https://www.pjrc.com/store/teensy.html)
|
[PJRC Teensy 2.0](https://www.pjrc.com/store/teensy.html)
|
||||||
@ -57,6 +91,7 @@ To use a Teensy 2.0 as an ISP flashing tool, you will first need to load a [spec
|
|||||||
|
|
||||||
!> Note that the `B0` pin on the Teensy should be wired to the `RESET` pin on the keyboard's controller. ***DO NOT*** connect the `RESET` pin on the Teensy to the `RESET` on the keyboard.
|
!> Note that the `B0` pin on the Teensy should be wired to the `RESET` pin on the keyboard's controller. ***DO NOT*** connect the `RESET` pin on the Teensy to the `RESET` on the keyboard.
|
||||||
|
|
||||||
|
|
||||||
### SparkFun PocketAVR / USBtinyISP
|
### SparkFun PocketAVR / USBtinyISP
|
||||||
|
|
||||||
[SparkFun PocketAVR](https://www.sparkfun.com/products/9825)
|
[SparkFun PocketAVR](https://www.sparkfun.com/products/9825)
|
||||||
|
@ -38,7 +38,6 @@
|
|||||||
* [キーマップの概要](ja/keymap.md)
|
* [キーマップの概要](ja/keymap.md)
|
||||||
* 開発環境
|
* 開発環境
|
||||||
* [Docker のガイド](ja/getting_started_docker.md)
|
* [Docker のガイド](ja/getting_started_docker.md)
|
||||||
* [Vagrant のガイド](ja/getting_started_vagrant.md)
|
|
||||||
* 書き込み
|
* 書き込み
|
||||||
* [書き込み](ja/flashing.md)
|
* [書き込み](ja/flashing.md)
|
||||||
* [ATmega32A の書き込み (ps2avrgb)](ja/flashing_bootloadhid.md)
|
* [ATmega32A の書き込み (ps2avrgb)](ja/flashing_bootloadhid.md)
|
||||||
@ -69,6 +68,7 @@
|
|||||||
* [モッドタップ](ja/mod_tap.md)
|
* [モッドタップ](ja/mod_tap.md)
|
||||||
* [マクロ](ja/feature_macros.md)
|
* [マクロ](ja/feature_macros.md)
|
||||||
* [マウスキー](ja/feature_mouse_keys.md)
|
* [マウスキー](ja/feature_mouse_keys.md)
|
||||||
|
* [Repeat Key](ja/feature_repeat_key.md)
|
||||||
* [Space Cadet Shift](ja/feature_space_cadet.md)
|
* [Space Cadet Shift](ja/feature_space_cadet.md)
|
||||||
* [US ANSI シフトキー](ja/keycodes_us_ansi_shifted.md)
|
* [US ANSI シフトキー](ja/keycodes_us_ansi_shifted.md)
|
||||||
|
|
||||||
|
@ -159,9 +159,6 @@ QMK での全ての利用可能な設定にはデフォルトがあります。
|
|||||||
* 詳細は [Permissive Hold](ja/tap_hold.md#permissive-hold) を見てください
|
* 詳細は [Permissive Hold](ja/tap_hold.md#permissive-hold) を見てください
|
||||||
* `#define PERMISSIVE_HOLD_PER_KEY`
|
* `#define PERMISSIVE_HOLD_PER_KEY`
|
||||||
* キーごとの `PERMISSIVE_HOLD` 設定の処理を有効にします
|
* キーごとの `PERMISSIVE_HOLD` 設定の処理を有効にします
|
||||||
* `#define IGNORE_MOD_TAP_INTERRUPT`
|
|
||||||
* 両方のキーに `TAPPING_TERM` を適用することで、ホールド時に他のキーに変換するキーを使ってローリングコンボ (zx) をすることができるようにします
|
|
||||||
* 詳細は [Ignore Mod Tap Interrupt](ja/tap_hold.md#ignore-mod-tap-interrupt) を見てください
|
|
||||||
* `#define TAPPING_FORCE_HOLD`
|
* `#define TAPPING_FORCE_HOLD`
|
||||||
* タップされた直後に、デュアルロールキーを修飾子として使用できるようにします
|
* タップされた直後に、デュアルロールキーを修飾子として使用できるようにします
|
||||||
* [Tapping Force Hold](ja/tap_hold.md#tapping-force-hold)を見てください
|
* [Tapping Force Hold](ja/tap_hold.md#tapping-force-hold)を見てください
|
||||||
@ -179,8 +176,6 @@ QMK での全ての利用可能な設定にはデフォルトがあります。
|
|||||||
* ワンショットがタイムアウトするまでの時間
|
* ワンショットがタイムアウトするまでの時間
|
||||||
* `#define ONESHOT_TAP_TOGGLE 2`
|
* `#define ONESHOT_TAP_TOGGLE 2`
|
||||||
* ワンショットトグルが引き起こされるまでのタップ数
|
* ワンショットトグルが引き起こされるまでのタップ数
|
||||||
* `#define COMBO_COUNT 2`
|
|
||||||
* [コンボ](ja/feature_combo.md)機能で使っているコンボの数にこれを設定します。
|
|
||||||
* `#define COMBO_TERM 200`
|
* `#define COMBO_TERM 200`
|
||||||
* コンボキーが検出されるまでの時間。定義されていない場合は、デフォルトは `TAPPING_TERM` です。
|
* コンボキーが検出されるまでの時間。定義されていない場合は、デフォルトは `TAPPING_TERM` です。
|
||||||
* `#define TAP_CODE_DELAY 100`
|
* `#define TAP_CODE_DELAY 100`
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
```c
|
```c
|
||||||
const uint16_t PROGMEM test_combo[] = {KC_A, KC_B, COMBO_END};
|
const uint16_t PROGMEM test_combo[] = {KC_A, KC_B, COMBO_END};
|
||||||
combo_t key_combos[COMBO_COUNT] = {COMBO(test_combo, KC_ESC)};
|
combo_t key_combos[] = {COMBO(test_combo, KC_ESC)};
|
||||||
```
|
```
|
||||||
|
|
||||||
これは、A と B のキーを押した場合に、"Escape" を送信します。
|
これは、A と B のキーを押した場合に、"Escape" を送信します。
|
||||||
@ -38,7 +38,7 @@ enum combos {
|
|||||||
const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END};
|
const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END};
|
||||||
const uint16_t PROGMEM jk_combo[] = {KC_J, KC_K, COMBO_END};
|
const uint16_t PROGMEM jk_combo[] = {KC_J, KC_K, COMBO_END};
|
||||||
|
|
||||||
combo_t key_combos[COMBO_COUNT] = {
|
combo_t key_combos[] = {
|
||||||
[AB_ESC] = COMBO(ab_combo, KC_ESC),
|
[AB_ESC] = COMBO(ab_combo, KC_ESC),
|
||||||
[JK_TAB] = COMBO(jk_combo, KC_TAB)
|
[JK_TAB] = COMBO(jk_combo, KC_TAB)
|
||||||
};
|
};
|
||||||
@ -55,7 +55,7 @@ enum combo_events {
|
|||||||
const uint16_t PROGMEM copy_combo[] = {KC_Z, KC_C, COMBO_END};
|
const uint16_t PROGMEM copy_combo[] = {KC_Z, KC_C, COMBO_END};
|
||||||
const uint16_t PROGMEM paste_combo[] = {KC_X, KC_V, COMBO_END};
|
const uint16_t PROGMEM paste_combo[] = {KC_X, KC_V, COMBO_END};
|
||||||
|
|
||||||
combo_t key_combos[COMBO_COUNT] = {
|
combo_t key_combos[] = {
|
||||||
[ZC_COPY] = COMBO_ACTION(copy_combo),
|
[ZC_COPY] = COMBO_ACTION(copy_combo),
|
||||||
[XV_PASTE] = COMBO_ACTION(paste_combo),
|
[XV_PASTE] = COMBO_ACTION(paste_combo),
|
||||||
};
|
};
|
||||||
|
@ -64,7 +64,7 @@ QMK はその場で作られた一時的なマクロをサポートします。
|
|||||||
|
|
||||||
direction がどのマクロであるかを示すことに注意してください。`1` がマクロ 1、`-1` がマクロ 2、0 がマクロ無しです。
|
direction がどのマクロであるかを示すことに注意してください。`1` がマクロ 1、`-1` がマクロ 2、0 がマクロ無しです。
|
||||||
|
|
||||||
* `dynamic_macro_record_start_user(void)` - マクロの記録を開始する時に起動されます。
|
* `dynamic_macro_record_start_user(int8_t direction)` - マクロの記録を開始する時に起動されます。
|
||||||
* `dynamic_macro_play_user(int8_t direction)` - マクロを再生する時に起動されます。
|
* `dynamic_macro_play_user(int8_t direction)` - マクロを再生する時に起動されます。
|
||||||
* `dynamic_macro_record_key_user(int8_t direction, keyrecord_t *record)` - マクロの記録中に各キー押下で起動されます。
|
* `dynamic_macro_record_key_user(int8_t direction, keyrecord_t *record)` - マクロの記録中に各キー押下で起動されます。
|
||||||
* `dynamic_macro_record_end_user(int8_t direction)` - マクロの記録を停止した時に起動されます。
|
* `dynamic_macro_record_end_user(int8_t direction)` - マクロの記録を停止した時に起動されます。
|
||||||
|
@ -80,7 +80,7 @@ bool process_steno_user(uint16_t keycode, keyrecord_t *record) { return true; }
|
|||||||
bool post_process_steno_user(uint16_t keycode, keyrecord_t *record, steno_mode_t mode, uint8_t chord[6], int8_t pressed);
|
bool post_process_steno_user(uint16_t keycode, keyrecord_t *record, steno_mode_t mode, uint8_t chord[6], int8_t pressed);
|
||||||
```
|
```
|
||||||
|
|
||||||
この関数はキーが処理された後、ただしコードを送信するかどうかを決める前に呼び出されます。`IS_PRESSED(record->event)` が false で、`pressed` が 0 または 1 の場合は、コードはまもなく送信されますが、まだ送信されてはいません。ここが速記コードあるいはキーのライブ表示などのフックを配置する場所です。
|
この関数はキーが処理された後、ただしコードを送信するかどうかを決める前に呼び出されます。`record->event.pressed` が false で、`pressed` が 0 または 1 の場合は、コードはまもなく送信されますが、まだ送信されてはいません。ここが速記コードあるいはキーのライブ表示などのフックを配置する場所です。
|
||||||
|
|
||||||
|
|
||||||
## キーコードリファレンス :id=keycode-reference
|
## キーコードリファレンス :id=keycode-reference
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
# Vagrant クイックスタート
|
|
||||||
|
|
||||||
<!---
|
|
||||||
original document: 0.12.43:docs/getting_started_vagrant.md
|
|
||||||
git diff 0.12.43 HEAD -- docs/getting_started_vagrant.md | cat
|
|
||||||
-->
|
|
||||||
|
|
||||||
このプロジェクトは、プライマリオペレーティングシステムに大きな変更を加えることなくキーボードの新しいファームウェアを非常に簡単に構築することができる `Vagrantfile` を含みます。これは、あなたがプロジェクトをクローンしビルドを実行した時に、ビルドのために Vagrantfile を使っている他のユーザと全く同じ環境を持つことも保証します。これにより、人々はあなたが遭遇した問題の解決をより簡単に行えるようになります。
|
|
||||||
|
|
||||||
## 必要事項
|
|
||||||
|
|
||||||
このリポジトリ内の `Vagrantfile` を使うには、[Vagrant](https://www.vagrantup.com/) およびサポートされるプロバイダがインストールされている必要があります:
|
|
||||||
|
|
||||||
* [VirtualBox](https://www.virtualbox.org/) (バージョン 5.0.12 以降)
|
|
||||||
* 「Vagrant を使うために最もアクセスしやすいプラットフォーム」とうたわれています。
|
|
||||||
* [VMware Workstation](https://www.vmware.com/products/workstation) および [Vagrant VMware プラグイン](https://www.vagrantup.com/vmware)
|
|
||||||
* (有料) VMware プラグインには、ライセンスされた VMware Workstation/Fusion のコピーが必要です。
|
|
||||||
* [Docker](https://www.docker.com/)
|
|
||||||
|
|
||||||
Vagrant 以外に、適切なプロバイダがインストールされ、その後におそらくコンピュータを再起動すると、このプロジェクトをチェックアウトしたフォルダ内の任意の場所で 'vagrant up' を単純に実行することができ、このプロジェクトをビルドするのに必要な全てのツールが含まれる環境(仮想マシンあるいはコンテナ)が開始されます。Vagrant 起動時にうまく始めるためのヒントが表示されますが、それ以外に、以下のビルドドキュメントを参照することもできます。
|
|
||||||
|
|
||||||
## ファームウェアの書き込み
|
|
||||||
|
|
||||||
ファームウェアを書き込む「簡単な」方法は、ホスト OS からツールを使うことです:
|
|
||||||
|
|
||||||
* [QMK Toolbox](https://github.com/qmk/qmk_toolbox) (推奨)
|
|
||||||
* [Teensy ローダー](https://www.pjrc.com/teensy/loader.html)
|
|
||||||
|
|
||||||
コマンドラインでプログラムしたい場合は、Vagranfile の ['modifyvm'] 行のコメントを解除して Linux への USB パススルーを有効にし、dfu-util/dfu-programmer のようなコマンドラインツールを使ってプログラムすることができます。あるいは Teensy CLI バージョンをインストールすることができます。
|
|
||||||
|
|
||||||
## Vagrantfile の概要
|
|
||||||
開発環境は QMK Docker イメージ、`qmkfm/qmk_cli` を実行するように設定されています。これはシステム間の予測可能性が保証されるだけでなく、CI 環境もミラーされます。
|
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
### Virtualbox で問題が発生するのはなぜですか?
|
|
||||||
Virtualbox 5 の特定のバージョンはこの Vagrantfile のボックスにインストールされている Virtualbox の拡張機能と互換性が無いようです。/vagrant のマウントで問題が発生した場合は、Virtualbox のバージョンを少なくとも 5.0.12 にアップグレードしてください。**または、以下のコマンドを実行してみることができます:**
|
|
||||||
|
|
||||||
```console
|
|
||||||
vagrant plugin install vagrant-vbguest
|
|
||||||
```
|
|
||||||
|
|
||||||
### 既存の環境を削除するにはどうすればいいですか?
|
|
||||||
あなたの環境での作業が完了しましたか?このプロジェクトをチェックアウトしたフォルダの中のどこからでも、以下を実行してください:
|
|
||||||
|
|
||||||
```console
|
|
||||||
vagrant destroy
|
|
||||||
```
|
|
||||||
|
|
||||||
### Docker を直接使いたい場合はどうしますか?
|
|
||||||
仮想マシン無しで Vagrant のワークフローを活用したいですか?Vagrantfile は仮想マシンの実行をバイパスし、コンテナを直接実行するように設定されています。Docker を強制的に使うように環境を立ち上げる場合は、以下を実行してください:
|
|
||||||
```console
|
|
||||||
vagrant up --provider=docker
|
|
||||||
```
|
|
||||||
|
|
||||||
### Docker コンテナではなく仮想マシンにアクセスするにはどうすればいいですか?
|
|
||||||
以下を実行して、公式の QMK ビルダーイメージから直接起動する `vagrant` ユーザをバイパスするようにします:
|
|
||||||
|
|
||||||
```console
|
|
||||||
vagrant ssh -c 'sudo -i'
|
|
||||||
```
|
|
@ -63,8 +63,6 @@ uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) {
|
|||||||
|
|
||||||
通常、これら全てを `TAPPING_TERM` (デフォルト: 200ms) 内で行うと、ファームウェアとホストシステムによって `ax` として登録されます。許容ホールドを有効にすると、別のキーがタップされた場合にモッドタップキーを修飾キーと見なすように処理を変更し、 `X` (`SHIFT`+`x`) と登録されます。
|
通常、これら全てを `TAPPING_TERM` (デフォルト: 200ms) 内で行うと、ファームウェアとホストシステムによって `ax` として登録されます。許容ホールドを有効にすると、別のキーがタップされた場合にモッドタップキーを修飾キーと見なすように処理を変更し、 `X` (`SHIFT`+`x`) と登録されます。
|
||||||
|
|
||||||
?> `モッドタップ割り込みの無視`を有効にしている場合、これにより両方の動きが変更されます。通常のキーには、最初のキーが最初に放された場合、あるいは両方のキーが `TAPPING_TERM` より長くホールドされた場合に、修飾キーが追加されます。
|
|
||||||
|
|
||||||
この機能をより細かく制御するために、以下を `config.h` に追加することができます:
|
この機能をより細かく制御するために、以下を `config.h` に追加することができます:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
@ -84,32 +82,6 @@ bool get_permissive_hold(uint16_t keycode, keyrecord_t *record) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## モッドタップ割り込みの無視
|
|
||||||
|
|
||||||
この設定を有効にするには、これを `config.h` に追加してください:
|
|
||||||
|
|
||||||
```c
|
|
||||||
#define IGNORE_MOD_TAP_INTERRUPT
|
|
||||||
```
|
|
||||||
|
|
||||||
許容ホールドと同様に、これは高速なタイピストのためのファームウェアの処理方法を変更します。モッドタップキーを押し、他のキーを押し、モッドタップキーを放し、通常のキーを放すと、`TAPPING_TERM` 内で押された場合でも、通常はモッドと通常のキーが出力されます。これは、ローリングコンボキーや、頻繁に使用するキー(例えば、`RCTL_T(KC_QUOT)`)にモッドタップを使う高速なタイピストには望ましくない場合があります。
|
|
||||||
|
|
||||||
`モッドタップ割り込みの無視`を設定するには、両方のキーを `TAPPING_TERM` の間ホールドすると、(その修飾キーの)ホールド機能を実行する必要があります。
|
|
||||||
|
|
||||||
例えば:
|
|
||||||
|
|
||||||
- `SFT_T(KC_A)` を押す
|
|
||||||
- `KC_X` を押す
|
|
||||||
- `SFT_T(KC_A)` を放す
|
|
||||||
- `KC_X` を放す
|
|
||||||
|
|
||||||
通常、これは大文字の `X` (`SHIFT`+`x`)、またはモッド + キーを送信します。`モッドタップ割り込みの無視` を有効にすると、ホールドアクションを登録するには、両方のキーを `TAPPING_TERM` の間ホールドする必要があります。この場合、素早いタップは `ax` を送信しますが、両方をホールドすると、大文字の `X` (`SHIFT`+`x`) を出力します。
|
|
||||||
|
|
||||||
|
|
||||||
?> __注意__: これはモディファイアにのみ関係し、レイヤー切り替えキーには関係しません。
|
|
||||||
|
|
||||||
?> `許容ホールド`を有効にすると、これは両方がどのように動作するかを変更します。通常のキーには、最初のキーが最初に放された場合、あるいは両方のキーが `TAPPING_TERM` より長くホールドされた場合に、修飾キーが追加されます。
|
|
||||||
|
|
||||||
## タッピング強制ホールド
|
## タッピング強制ホールド
|
||||||
|
|
||||||
`タッピング強制ホールド` を有効にするには、以下を `config.h` に追加します:
|
`タッピング強制ホールド` を有効にするには、以下を `config.h` に追加します:
|
||||||
|
@ -803,6 +803,15 @@ See also: [Programmable Button](feature_programmable_button.md)
|
|||||||
|`QK_PROGRAMMABLE_BUTTON_31`|`PB_31`|Programmable button 31|
|
|`QK_PROGRAMMABLE_BUTTON_31`|`PB_31`|Programmable button 31|
|
||||||
|`QK_PROGRAMMABLE_BUTTON_32`|`PB_32`|Programmable button 32|
|
|`QK_PROGRAMMABLE_BUTTON_32`|`PB_32`|Programmable button 32|
|
||||||
|
|
||||||
|
## Repeat Key :id=repeat-key
|
||||||
|
|
||||||
|
See also: [Repeat Key](feature_repeat_key.md)
|
||||||
|
|
||||||
|
|Keycode |Aliases |Description |
|
||||||
|
|-----------------------|---------|-------------------------------------|
|
||||||
|
|`QK_REPEAT_KEY` |`QK_REP` |Repeat the last pressed key |
|
||||||
|
|`QK_ALT_REPEAT_KEY` |`QK_AREP`|Perform alternate of the last key |
|
||||||
|
|
||||||
## Space Cadet :id=space-cadet
|
## Space Cadet :id=space-cadet
|
||||||
|
|
||||||
See also: [Space Cadet](feature_space_cadet.md)
|
See also: [Space Cadet](feature_space_cadet.md)
|
||||||
|
@ -111,8 +111,6 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Enabling `IGNORE_MOD_TAP_INTERRUPT` is recommended when using Mod-Tap on alphanumeric keys to avoid hold function taking precendence when the next key is pressed quickly. See [Ignore Mod Tap Interrupt](tap_hold.md#ignore-mod-tap-interrupt) for more details.
|
|
||||||
|
|
||||||
### Changing both tap and hold
|
### Changing both tap and hold
|
||||||
|
|
||||||
This last example implements custom tap and hold function with `LT(0,KC_NO)` to create a single copy-on-tap, paste-on-hold key:
|
This last example implements custom tap and hold function with `LT(0,KC_NO)` to create a single copy-on-tap, paste-on-hold key:
|
||||||
|
@ -95,7 +95,7 @@ on: [push, workflow_dispatch]
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: qmkfm/qmk_cli
|
container: ghcr.io/qmk/qmk_cli
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
@ -56,11 +56,17 @@ QMK maintains a Homebrew tap and formula which will automatically install the CL
|
|||||||
|
|
||||||
You will need to install Homebrew. Follow the instructions on https://brew.sh.
|
You will need to install Homebrew. Follow the instructions on https://brew.sh.
|
||||||
|
|
||||||
|
!> **NOTE:** If you are using Apple Silicon, such as the M1, you will need to install a rosetta compatible version of Homebrew. This version does not override the base Homebrew. This can be done by running `arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"`. See here: [Rosetta-compatible Homebrew](https://stackoverflow.com/questions/64882584/how-to-run-the-homebrew-installer-under-rosetta-2-on-m1-macbook)
|
||||||
|
|
||||||
#### Installation
|
#### Installation
|
||||||
|
|
||||||
Install the QMK CLI by running:
|
Install the QMK CLI by running:
|
||||||
|
|
||||||
brew install qmk/qmk/qmk
|
brew install qmk/qmk/qmk
|
||||||
|
|
||||||
|
Install the QMK CLI on an Apple Silicon Mac by running:
|
||||||
|
|
||||||
|
arch -x86_64 brew install qmk/qmk/qmk
|
||||||
|
|
||||||
### ** Linux/WSL **
|
### ** Linux/WSL **
|
||||||
|
|
||||||
@ -166,6 +172,8 @@ For example, to build a firmware for a Clueboard 66% you would use:
|
|||||||
|
|
||||||
qmk compile -kb clueboard/66/rev3 -km default
|
qmk compile -kb clueboard/66/rev3 -km default
|
||||||
|
|
||||||
|
?> The keyboard option is the path relative to the keyboard directory, the above example would be found in `qmk_firmware/keyboards/clueboard/66/rev3`. If you're unsure you can view a full list of supported keyboards with `qmk list-keyboards`.
|
||||||
|
|
||||||
When it is done you should have a lot of output that ends similar to this:
|
When it is done you should have a lot of output that ends similar to this:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -46,16 +46,7 @@ Before starting, you will want to make sure that you have all of the build tools
|
|||||||
|
|
||||||
This part is super simple. However, there is some configuration that we need to do to ensure things are configured correctly.
|
This part is super simple. However, there is some configuration that we need to do to ensure things are configured correctly.
|
||||||
|
|
||||||
### Configuring VS Code
|
#### MSYS2 Setup
|
||||||
|
|
||||||
First, we need to set up IntelliSense. This isn't strictly required, but it will make your life a LOT easier. To do this, we need to create the `.vscode/c_cpp_properties.json` file in the QMK Firmware folder, You can do this all manually, but I've done most of the work already.
|
|
||||||
|
|
||||||
Grab [this file](https://gist.github.com/drashna/48e2c49ce877be592a1650f91f8473e8) and save it. You may need to edit this file, if you didn't install MSYS2 to the default location, or are using WSL/LxSS.
|
|
||||||
|
|
||||||
Once you have saved this file, you will need to reload VS Code, if it was already running.
|
|
||||||
|
|
||||||
?> You should see an `extensions.json` and `settings.json` file in the `.vscode` folder, as well.
|
|
||||||
|
|
||||||
|
|
||||||
Now, we will set up the MSYS2 window to show up in VSCode as the integrated terminal. This has a number of advantages. Mostly, you can control+click on errors and jump to those files. This makes debugging much easier. It's also nice, in that you don't have to jump to another window.
|
Now, we will set up the MSYS2 window to show up in VSCode as the integrated terminal. This has a number of advantages. Mostly, you can control+click on errors and jump to those files. This makes debugging much easier. It's also nice, in that you don't have to jump to another window.
|
||||||
|
|
||||||
@ -101,17 +92,28 @@ No, really, that's it. The paths needed are already included when installing th
|
|||||||
|
|
||||||
There are a number of extensions that you may want to install:
|
There are a number of extensions that you may want to install:
|
||||||
|
|
||||||
* [Git Extension Pack](https://marketplace.visualstudio.com/items?itemName=donjayamanne.git-extension-pack) -
|
* [Git Extension Pack](https://marketplace.visualstudio.com/items?itemName=donjayamanne.git-extension-pack) - This installs a bunch of Git related tools that may make using Git with QMK Firmware easier.
|
||||||
This installs a bunch of Git related tools that may make using Git with QMK Firmware easier.
|
* [clangd](https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd) - _[Optional]_ - This is the language server for C/C++ that VS Code uses. It provides IntelliSense and other features.
|
||||||
* [EditorConfig for VS Code](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) - _[Optional]_ - Helps to keep the code to the QMK Coding Conventions.
|
* [EditorConfig for VS Code](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) - _[Optional]_ - Helps to keep the code to the QMK Coding Conventions.
|
||||||
* [GitHub Markdown Preview](https://marketplace.visualstudio.com/items?itemName=bierner.github-markdown-preview) - _[Optional]_ - Makes the markdown preview in VS Code more like GitHub's.
|
* [GitHub Markdown Preview](https://marketplace.visualstudio.com/items?itemName=bierner.github-markdown-preview) - _[Optional]_ - Makes the markdown preview in VS Code more like GitHub's.
|
||||||
* [VS Live Share Extension Pack](https://marketplace.visualstudio.com/items?itemName=MS-vsliveshare.vsliveshare-pack) - _[Optional]_ - This extension allows somebody else to access your workspace (or you to access somebody else's workspace) and help out. This is great if you're having issues and need some help from somebody.
|
* [VS Live Share Extension Pack](https://marketplace.visualstudio.com/items?itemName=MS-vsliveshare.vsliveshare-pack) - _[Optional]_ - This extension allows somebody else to access your workspace (or you to access somebody else's workspace) and help out. This is great if you're having issues and need some help from somebody.
|
||||||
|
|
||||||
Restart once you've installed any extensions
|
Restart once you've installed any extensions.
|
||||||
|
|
||||||
# Configure VS Code for QMK
|
# Configure VS Code for QMK
|
||||||
|
|
||||||
1. Click <kbd><kbd>File</kbd> > <kbd>Open Folder</kbd></kbd>
|
1. Click <kbd><kbd>File</kbd> > <kbd>Open Folder</kbd></kbd>
|
||||||
2. Open the QMK Firmware folder that you cloned from GitHub.
|
2. Open the QMK Firmware folder that you cloned from GitHub.
|
||||||
3. Click <kbd><kbd>File</kbd> > <kbd>Save Workspace As...</kbd></kbd>
|
3. Click <kbd><kbd>File</kbd> > <kbd>Save Workspace As...</kbd></kbd>
|
||||||
|
|
||||||
And now you're ready to code QMK Firmware in VS Code
|
## Configuring VS Code
|
||||||
|
|
||||||
|
Using the [standard `compile_commands.json` database](https://clang.llvm.org/docs/JSONCompilationDatabase.html), we can get the VS code _clangd_ extension to use the correct includes and defines used for your keyboard and keymap.
|
||||||
|
|
||||||
|
1. Run `qmk generate-compilation-database -kb <keyboard> -km <keymap>` to generate the `compile_commands.json`.
|
||||||
|
1. Inside VS code, press <kbd><kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd></kbd> (macOS: <kbd><kbd>Command</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd></kbd>) to open the command palette.
|
||||||
|
1. Start typing `clangd: Download Language Server` and select it when it appears. Note that this only needs to be done once on clangd extension installation, if it didn't already ask to do so.
|
||||||
|
1. Inside VS code, press <kbd><kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd></kbd> (macOS: <kbd><kbd>Command</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd></kbd>) to open the command palette.
|
||||||
|
1. Start typing `clangd: Restart Language Server` and select it when it appears.
|
||||||
|
|
||||||
|
Now you're ready to code QMK Firmware in VS Code!
|
||||||
|
@ -53,6 +53,7 @@ https://github.com/qmk/qmk_firmware/pulls?q=is%3Apr+is%3Aclosed+label%3Akeyboard
|
|||||||
|
|
||||||
- keyboard moves within the repository *must* go through the `develop` branch instead of `master`, so as to ensure compatibility for users
|
- keyboard moves within the repository *must* go through the `develop` branch instead of `master`, so as to ensure compatibility for users
|
||||||
- `data/mappings/keyboard_aliases.hjson` must be updated to reflect the move, so users with pre-created configurator keymap.json files continue to detect the correct keyboard
|
- `data/mappings/keyboard_aliases.hjson` must be updated to reflect the move, so users with pre-created configurator keymap.json files continue to detect the correct keyboard
|
||||||
|
- keyboard updates and refactors (eg. to data driven) *must* go through `develop` to reduce `master` -> `develop` merge conflicts
|
||||||
- PR submissions from a `kbfirmware` export (or equivalent) will not be accepted unless converted to new QMK standards -- try `qmk import-kbfirmware` first
|
- PR submissions from a `kbfirmware` export (or equivalent) will not be accepted unless converted to new QMK standards -- try `qmk import-kbfirmware` first
|
||||||
- `info.json`
|
- `info.json`
|
||||||
- With the move to [data driven](https://docs.qmk.fm/#/data_driven_config) keyboard configuration, we encourage contributors to utilise as many features as possible of the info.json [schema](https://github.com/qmk/qmk_firmware/blob/master/data/schemas/keyboard.jsonschema).
|
- With the move to [data driven](https://docs.qmk.fm/#/data_driven_config) keyboard configuration, we encourage contributors to utilise as many features as possible of the info.json [schema](https://github.com/qmk/qmk_firmware/blob/master/data/schemas/keyboard.jsonschema).
|
||||||
@ -61,7 +62,7 @@ https://github.com/qmk/qmk_firmware/pulls?q=is%3Apr+is%3Aclosed+label%3Akeyboard
|
|||||||
- valid maintainer
|
- valid maintainer
|
||||||
- valid USB VID/PID and device version
|
- valid USB VID/PID and device version
|
||||||
- displays correctly in Configurator (press Ctrl+Shift+I to preview local file, turn on fast input to verify ordering)
|
- displays correctly in Configurator (press Ctrl+Shift+I to preview local file, turn on fast input to verify ordering)
|
||||||
- `layout` definitions should include matrix positions, so that `LAYOUT` macros can be generated at build time
|
- `layout` definitions must include matrix positions, so that `LAYOUT` macros can be generated at build time
|
||||||
- should use standard definitions if applicable
|
- should use standard definitions if applicable
|
||||||
- use the Community Layout macro names where they apply (preferred above `LAYOUT`/`LAYOUT_all`)
|
- use the Community Layout macro names where they apply (preferred above `LAYOUT`/`LAYOUT_all`)
|
||||||
- If the keyboard only has a single electrical/switch layout:
|
- If the keyboard only has a single electrical/switch layout:
|
||||||
@ -123,7 +124,7 @@ https://github.com/qmk/qmk_firmware/pulls?q=is%3Apr+is%3Aclosed+label%3Akeyboard
|
|||||||
- hardware that's enabled at the keyboard level and requires configuration such as OLED displays or encoders should have basic functionality implemented here
|
- hardware that's enabled at the keyboard level and requires configuration such as OLED displays or encoders should have basic functionality implemented here
|
||||||
- `<keyboard>.h`
|
- `<keyboard>.h`
|
||||||
- `#include "quantum.h"` appears at the top
|
- `#include "quantum.h"` appears at the top
|
||||||
- `LAYOUT` macros should be moved to `info.json`
|
- `LAYOUT` macros are no longer accepted and should instead be moved to `info.json`
|
||||||
- keymap `config.h`
|
- keymap `config.h`
|
||||||
- no duplication of `rules.mk` or `config.h` from keyboard
|
- no duplication of `rules.mk` or `config.h` from keyboard
|
||||||
- `keymaps/default/keymap.c`
|
- `keymaps/default/keymap.c`
|
||||||
|
@ -32,16 +32,20 @@ Supported devices:
|
|||||||
|
|
||||||
## Quantum Painter Configuration :id=quantum-painter-config
|
## Quantum Painter Configuration :id=quantum-painter-config
|
||||||
|
|
||||||
| Option | Default | Purpose |
|
| Option | Default | Purpose |
|
||||||
|------------------------------------------|---------|---------------------------------------------------------------------------------------------------------------------------------------------|
|
|---------------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `QUANTUM_PAINTER_NUM_IMAGES` | `8` | The maximum number of images/animations that can be loaded at any one time. |
|
| `QUANTUM_PAINTER_DISPLAY_TIMEOUT` | `30000` | This controls the amount of time (in milliseconds) that all displays will remain on after the last user input. If set to `0`, the display will remain on indefinitely. |
|
||||||
| `QUANTUM_PAINTER_NUM_FONTS` | `4` | The maximum number of fonts that can be loaded at any one time. |
|
| `QUANTUM_PAINTER_TASK_THROTTLE` | `1` | This controls the amount of time (in milliseconds) that the Quantum Painter internal task will wait between each execution. Affects animations, display timeout, and LVGL timing if enabled. |
|
||||||
| `QUANTUM_PAINTER_CONCURRENT_ANIMATIONS` | `4` | The maximum number of animations that can be executed at the same time. |
|
| `QUANTUM_PAINTER_NUM_IMAGES` | `8` | The maximum number of images/animations that can be loaded at any one time. |
|
||||||
| `QUANTUM_PAINTER_LOAD_FONTS_TO_RAM` | `FALSE` | Whether or not fonts should be loaded to RAM. Relevant for fonts stored in off-chip persistent storage, such as external flash. |
|
| `QUANTUM_PAINTER_NUM_FONTS` | `4` | The maximum number of fonts that can be loaded at any one time. |
|
||||||
| `QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE` | `32` | The limit of the amount of pixel data that can be transmitted in one transaction to the display. Higher values require more RAM on the MCU. |
|
| `QUANTUM_PAINTER_CONCURRENT_ANIMATIONS` | `4` | The maximum number of animations that can be executed at the same time. |
|
||||||
| `QUANTUM_PAINTER_SUPPORTS_256_PALETTE` | `FALSE` | If 256-color palettes are supported. Requires significantly more RAM on the MCU. |
|
| `QUANTUM_PAINTER_LOAD_FONTS_TO_RAM` | `FALSE` | Whether or not fonts should be loaded to RAM. Relevant for fonts stored in off-chip persistent storage, such as external flash. |
|
||||||
| `QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS` | `FALSE` | If native color range is supported. Requires significantly more RAM on the MCU. |
|
| `QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE` | `32` | The limit of the amount of pixel data that can be transmitted in one transaction to the display. Higher values require more RAM on the MCU. |
|
||||||
| `QUANTUM_PAINTER_DEBUG` | _unset_ | Prints out significant amounts of debugging information to CONSOLE output. Significant performance degradation, use only for debugging. |
|
| `QUANTUM_PAINTER_SUPPORTS_256_PALETTE` | `FALSE` | If 256-color palettes are supported. Requires significantly more RAM on the MCU. |
|
||||||
|
| `QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS` | `FALSE` | If native color range is supported. Requires significantly more RAM on the MCU. |
|
||||||
|
| `QUANTUM_PAINTER_DEBUG` | _unset_ | Prints out significant amounts of debugging information to CONSOLE output. Significant performance degradation, use only for debugging. |
|
||||||
|
| `QUANTUM_PAINTER_DEBUG_ENABLE_FLUSH_TASK_OUTPUT` | _unset_ | By default, debug output is disabled while the internal task is flushing the display(s). If you want to keep it enabled, add this to your `config.h`. Note: Console will get clogged. |
|
||||||
|
|
||||||
|
|
||||||
Drivers have their own set of configurable options, and are described in their respective sections.
|
Drivers have their own set of configurable options, and are described in their respective sections.
|
||||||
|
|
||||||
|
@ -194,6 +194,7 @@ That said, there are a number of Pro Micro replacements with ARM controllers:
|
|||||||
* [Blok](https://boardsource.xyz/store/628b95b494dfa308a6581622)
|
* [Blok](https://boardsource.xyz/store/628b95b494dfa308a6581622)
|
||||||
* [Elite-Pi](https://keeb.io/products/elite-pi-usb-c-pro-micro-replacement-rp2040)
|
* [Elite-Pi](https://keeb.io/products/elite-pi-usb-c-pro-micro-replacement-rp2040)
|
||||||
* [0xCB Helios](https://keeb.supply/products/0xcb-helios) ([Open Source](https://github.com/0xCB-dev/0xCB-Helios), DIY/PCBA/Shop)
|
* [0xCB Helios](https://keeb.supply/products/0xcb-helios) ([Open Source](https://github.com/0xCB-dev/0xCB-Helios), DIY/PCBA/Shop)
|
||||||
|
* [Liatris](https://splitkb.com/products/liatris)
|
||||||
* [Michi](https://github.com/ci-bus/michi-promicro-rp2040)
|
* [Michi](https://github.com/ci-bus/michi-promicro-rp2040)
|
||||||
|
|
||||||
There are other, non-Pro Micro compatible boards out there. The most popular being:
|
There are other, non-Pro Micro compatible boards out there. The most popular being:
|
||||||
|
@ -130,20 +130,18 @@ Note that until the tap-or-hold decision completes (which happens when either th
|
|||||||
|
|
||||||
To better illustrate the tap-or-hold decision modes, let us compare the expected output of each decision mode in a handful of tapping scenarios involving a mod-tap key (`LSFT_T(KC_A)`) and a regular key (`KC_B`) with the `TAPPING_TERM` set to 200ms.
|
To better illustrate the tap-or-hold decision modes, let us compare the expected output of each decision mode in a handful of tapping scenarios involving a mod-tap key (`LSFT_T(KC_A)`) and a regular key (`KC_B`) with the `TAPPING_TERM` set to 200ms.
|
||||||
|
|
||||||
By default, mod-taps behave like `HOLD_ON_OTHER_KEY_PRESS`, while layer-taps behave like "Ignore Interrupt" out of the box. If you want "Ignore Interrupt"-like behaviour for mod-taps, you must enable `IGNORE_MOD_TAP_INTERRUPT`, or return `false` in the `get_hold_on_other_key_press` function for all mod-taps.
|
|
||||||
|
|
||||||
Note: "`kc` held" in the "Physical key event" column means that the key wasn't physically released yet at this point in time.
|
Note: "`kc` held" in the "Physical key event" column means that the key wasn't physically released yet at this point in time.
|
||||||
|
|
||||||
#### Distinct taps (AABB) :id=distinct-taps
|
#### Distinct taps (AABB) :id=distinct-taps
|
||||||
|
|
||||||
| Time | Physical key event |Ignore Interrupt| `PERMISSIVE_HOLD` | `HOLD_ON_OTHER_KEY_PRESS` |
|
| Time | Physical key event | Default | `PERMISSIVE_HOLD` | `HOLD_ON_OTHER_KEY_PRESS` |
|
||||||
|------|--------------------|----------------|-------------------|----------------------------|
|
|------|--------------------|----------------|-------------------|----------------------------|
|
||||||
| 0 | `LSFT_T(KC_A)` down| | | |
|
| 0 | `LSFT_T(KC_A)` down| | | |
|
||||||
| 199 | `LSFT_T(KC_A)` up | a | a | a |
|
| 199 | `LSFT_T(KC_A)` up | a | a | a |
|
||||||
| 210 | `KC_B` down | ab | ab | ab |
|
| 210 | `KC_B` down | ab | ab | ab |
|
||||||
| 220 | `KC_B` up | ab | ab | ab |
|
| 220 | `KC_B` up | ab | ab | ab |
|
||||||
|
|
||||||
| Time | Physical key event |Ignore Interrupt| `PERMISSIVE_HOLD` | `HOLD_ON_OTHER_KEY_PRESS` |
|
| Time | Physical key event | Default | `PERMISSIVE_HOLD` | `HOLD_ON_OTHER_KEY_PRESS` |
|
||||||
|------|--------------------|----------------|-------------------|----------------------------|
|
|------|--------------------|----------------|-------------------|----------------------------|
|
||||||
| 0 | `LSFT_T(KC_A)` down| | | |
|
| 0 | `LSFT_T(KC_A)` down| | | |
|
||||||
| 200 | `LSFT_T(KC_A)` held|<kbd>Shift</kbd>| <kbd>Shift</kbd> | <kbd>Shift</kbd> |
|
| 200 | `LSFT_T(KC_A)` held|<kbd>Shift</kbd>| <kbd>Shift</kbd> | <kbd>Shift</kbd> |
|
||||||
@ -153,14 +151,14 @@ Note: "`kc` held" in the "Physical key event" column means that the key wasn't p
|
|||||||
|
|
||||||
#### Nested tap (ABBA) :id=nested-tap
|
#### Nested tap (ABBA) :id=nested-tap
|
||||||
|
|
||||||
| Time | Physical key event |Ignore Interrupt| `PERMISSIVE_HOLD` | `HOLD_ON_OTHER_KEY_PRESS` |
|
| Time | Physical key event | Default | `PERMISSIVE_HOLD` | `HOLD_ON_OTHER_KEY_PRESS` |
|
||||||
|------|--------------------|----------------|-------------------|----------------------------|
|
|------|--------------------|----------------|-------------------|----------------------------|
|
||||||
| 0 | `LSFT_T(KC_A)` down| | | |
|
| 0 | `LSFT_T(KC_A)` down| | | |
|
||||||
| 110 | `KC_B` down | | | B |
|
| 110 | `KC_B` down | | | B |
|
||||||
| 120 | `KC_B` up | | B | B |
|
| 120 | `KC_B` up | | B | B |
|
||||||
| 199 | `LSFT_T(KC_A)` up | ab | B | B |
|
| 199 | `LSFT_T(KC_A)` up | ab | B | B |
|
||||||
|
|
||||||
| Time | Physical key event |Ignore Interrupt| `PERMISSIVE_HOLD` | `HOLD_ON_OTHER_KEY_PRESS` |
|
| Time | Physical key event | Default | `PERMISSIVE_HOLD` | `HOLD_ON_OTHER_KEY_PRESS` |
|
||||||
|------|--------------------|----------------|-------------------|----------------------------|
|
|------|--------------------|----------------|-------------------|----------------------------|
|
||||||
| 0 | `LSFT_T(KC_A)` down| | | |
|
| 0 | `LSFT_T(KC_A)` down| | | |
|
||||||
| 110 | `KC_B` down | | | B |
|
| 110 | `KC_B` down | | | B |
|
||||||
@ -168,7 +166,7 @@ Note: "`kc` held" in the "Physical key event" column means that the key wasn't p
|
|||||||
| 200 | `LSFT_T(KC_A)` held| B | B | B |
|
| 200 | `LSFT_T(KC_A)` held| B | B | B |
|
||||||
| 210 | `LSFT_T(KC_A)` up | B | B | B |
|
| 210 | `LSFT_T(KC_A)` up | B | B | B |
|
||||||
|
|
||||||
| Time | Physical key event |Ignore Interrupt| `PERMISSIVE_HOLD` | `HOLD_ON_OTHER_KEY_PRESS` |
|
| Time | Physical key event | Default | `PERMISSIVE_HOLD` | `HOLD_ON_OTHER_KEY_PRESS` |
|
||||||
|------|--------------------|----------------|-------------------|----------------------------|
|
|------|--------------------|----------------|-------------------|----------------------------|
|
||||||
| 0 | `LSFT_T(KC_A)` down| | | |
|
| 0 | `LSFT_T(KC_A)` down| | | |
|
||||||
| 200 | `LSFT_T(KC_A)` held|<kbd>Shift</kbd>| <kbd>Shift</kbd> | <kbd>Shift</kbd> |
|
| 200 | `LSFT_T(KC_A)` held|<kbd>Shift</kbd>| <kbd>Shift</kbd> | <kbd>Shift</kbd> |
|
||||||
@ -178,14 +176,14 @@ Note: "`kc` held" in the "Physical key event" column means that the key wasn't p
|
|||||||
|
|
||||||
#### Rolling keys (ABAB) :id=rolling-keys
|
#### Rolling keys (ABAB) :id=rolling-keys
|
||||||
|
|
||||||
| Time | Physical key event |Ignore Interrupt| `PERMISSIVE_HOLD` | `HOLD_ON_OTHER_KEY_PRESS` |
|
| Time | Physical key event | Default | `PERMISSIVE_HOLD` | `HOLD_ON_OTHER_KEY_PRESS` |
|
||||||
|------|--------------------|----------------|-------------------|----------------------------|
|
|------|--------------------|----------------|-------------------|----------------------------|
|
||||||
| 0 | `LSFT_T(KC_A)` down| | | |
|
| 0 | `LSFT_T(KC_A)` down| | | |
|
||||||
| 110 | `KC_B` down | | | B |
|
| 110 | `KC_B` down | | | B |
|
||||||
| 130 | `LSFT_T(KC_A)` up | ab | ab | B |
|
| 130 | `LSFT_T(KC_A)` up | ab | ab | B |
|
||||||
| 140 | `KC_B` up | ab | ab | B |
|
| 140 | `KC_B` up | ab | ab | B |
|
||||||
|
|
||||||
| Time | Physical key event |Ignore Interrupt| `PERMISSIVE_HOLD` | `HOLD_ON_OTHER_KEY_PRESS` |
|
| Time | Physical key event | Default | `PERMISSIVE_HOLD` | `HOLD_ON_OTHER_KEY_PRESS` |
|
||||||
|------|--------------------|----------------|-------------------|----------------------------|
|
|------|--------------------|----------------|-------------------|----------------------------|
|
||||||
| 0 | `LSFT_T(KC_A)` down| | | |
|
| 0 | `LSFT_T(KC_A)` down| | | |
|
||||||
| 110 | `KC_B` down | | | B |
|
| 110 | `KC_B` down | | | B |
|
||||||
@ -241,10 +239,8 @@ Example sequence 3 (Mod Tap):
|
|||||||
| +--------------+ | |
|
| +--------------+ | |
|
||||||
+---------------------------|--------+
|
+---------------------------|--------+
|
||||||
```
|
```
|
||||||
Based on previous examples, you might have expected the output of the above sequence to be `KC_A` `KC_X`
|
In the above sequence, `SFT_T(KC_A)` has been released before the end of its `TAPPING_TERM` and as such will be interpreted as `KC_A`,
|
||||||
since `SFT_T(KC_A)` is NOT held longer than the `TAPPING_TERM`.
|
followed by any key event that happened after the initial press of `SFT_T(KC_A)`. In this instance, the output would be `KC_A` `KC_X`.
|
||||||
However, the actual output would be capital `X` (`SHIFT` + `x`) due to reasons
|
|
||||||
explained under [Ignore Mod Tap Interrupt](#ignore-mod-tap-interrupt).
|
|
||||||
|
|
||||||
### Permissive Hold
|
### Permissive Hold
|
||||||
|
|
||||||
@ -300,7 +296,7 @@ However, this slightly different sequence will not be affected by the “permiss
|
|||||||
|
|
||||||
In the sequence above the dual-role key is released before the other key is released, and if that happens within the tapping term, the “permissive hold” mode will still choose the tap action for the dual-role key, and the sequence will be registered as `al` by the host. We could describe this as a “rolling press” (the two keys' key down and key up events behave as if you were rolling a ball across the two keys, first pressing each key down in sequence and then releasing them in the same order).
|
In the sequence above the dual-role key is released before the other key is released, and if that happens within the tapping term, the “permissive hold” mode will still choose the tap action for the dual-role key, and the sequence will be registered as `al` by the host. We could describe this as a “rolling press” (the two keys' key down and key up events behave as if you were rolling a ball across the two keys, first pressing each key down in sequence and then releasing them in the same order).
|
||||||
|
|
||||||
?> The `PERMISSIVE_HOLD` option is not noticeable if you also enable `HOLD_ON_OTHER_KEY_PRESS` because the latter option considers both the “nested tap” and “rolling press” sequences like shown above as a hold action, not the tap action. `HOLD_ON_OTHER_KEY_PRESS` makes the Tap-Or-Hold decision earlier in the chain of key events, thus taking a precedence over `PERMISSIVE_HOLD`. This remark also applies to default mod-taps.
|
?> The `PERMISSIVE_HOLD` option is not noticeable if you also enable `HOLD_ON_OTHER_KEY_PRESS` because the latter option considers both the “nested tap” and “rolling press” sequences like shown above as a hold action, not the tap action. `HOLD_ON_OTHER_KEY_PRESS` makes the Tap-Or-Hold decision earlier in the chain of key events, thus taking a precedence over `PERMISSIVE_HOLD`.
|
||||||
|
|
||||||
For more granular control of this feature, you can add the following to your `config.h`:
|
For more granular control of this feature, you can add the following to your `config.h`:
|
||||||
|
|
||||||
@ -356,8 +352,6 @@ An example of a sequence that is affected by the “hold on other key press” m
|
|||||||
|
|
||||||
Normally, if you do all this within the `TAPPING_TERM` (default: 200ms), this will be registered as `al` by the firmware and host system. With the `HOLD_ON_OTHER_KEY_PRESS` option enabled, the Layer Tap key is considered as a layer switch if another key is pressed, and the above sequence would be registered as `KC_RGHT` (the mapping of `L` on layer 2).
|
Normally, if you do all this within the `TAPPING_TERM` (default: 200ms), this will be registered as `al` by the firmware and host system. With the `HOLD_ON_OTHER_KEY_PRESS` option enabled, the Layer Tap key is considered as a layer switch if another key is pressed, and the above sequence would be registered as `KC_RGHT` (the mapping of `L` on layer 2).
|
||||||
|
|
||||||
?> The `HOLD_ON_OTHER_KEY_PRESS` option is essentially redundant with the default mod-tap behaviour. The only notable difference is that `HOLD_ON_OTHER_KEY_PRESS` reduces the delay before the key events are made visible to the host.
|
|
||||||
|
|
||||||
For more granular control of this feature, you can add the following to your `config.h`:
|
For more granular control of this feature, you can add the following to your `config.h`:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
@ -379,73 +373,6 @@ bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Ignore Mod Tap Interrupt
|
|
||||||
|
|
||||||
To enable this setting, add this to your `config.h`:
|
|
||||||
|
|
||||||
```c
|
|
||||||
#define IGNORE_MOD_TAP_INTERRUPT
|
|
||||||
```
|
|
||||||
|
|
||||||
?> This option affects only the Mod Tap keys; it does not affect other dual-role keys such as Layer Tap.
|
|
||||||
|
|
||||||
By default, the tap-or-hold decision for Mod Tap keys strongly prefers the hold action. If you press a Mod Tap key, then press another key while still holding the Mod Tap key down, the Mod Tap press will be handled as a modifier hold even if the Mod Tap key is then released within the tapping term, and irrespective of the order in which those keys are released. Using options such as `PERMISSIVE_HOLD` or `HOLD_ON_OTHER_KEY_PRESS` will not affect the functionality of Mod Tap keys in a major way (these options would still affect the delay until the common code for dual-role keys finishes its tap-or-hold decision, but then the special code for Mod Tap keys will override the result of that decision and choose the hold action if another key was pressed). In fact, by default, the tap-or-hold decision for Mod Tap keys is done in the same way as if the `HOLD_ON_OTHER_KEY_PRESS` option was enabled, but without the decreased delay provided by `HOLD_ON_OTHER_KEY_PRESS`.
|
|
||||||
|
|
||||||
If the `IGNORE_MOD_TAP_INTERRUPT` option is enabled, Mod Tap keys are no longer treated as a special case, and their behavior will match the behavior of other dual-role keys such as Layer Tap. Then the behavior of Mod Tap keys can be further tuned using other options such as `PERMISSIVE_HOLD` or `HOLD_ON_OTHER_KEY_PRESS`.
|
|
||||||
|
|
||||||
An example of a sequence that will be affected by the `IGNORE_MOD_TAP_INTERRUPT` option (assuming that options like `PERMISSIVE_HOLD` or `HOLD_ON_OTHER_KEY_PRESS` are not enabled):
|
|
||||||
|
|
||||||
- `SFT_T(KC_A)` Down
|
|
||||||
- `KC_X` Down
|
|
||||||
- `SFT_T(KC_A)` Up
|
|
||||||
- `KC_X` Up
|
|
||||||
|
|
||||||
```
|
|
||||||
TAPPING_TERM
|
|
||||||
+---------------------------|--------+
|
|
||||||
| +-------------+ | |
|
|
||||||
| | SFT_T(KC_A) | | |
|
|
||||||
| +-------------+ | |
|
|
||||||
| +--------------+ | |
|
|
||||||
| | KC_X | | |
|
|
||||||
| +--------------+ | |
|
|
||||||
+---------------------------|--------+
|
|
||||||
```
|
|
||||||
|
|
||||||
Normally, this would send a capital `X` (`SHIFT`+`x`), even if the sequence is performed faster than the `TAPPING_TERM`. However, if the `IGNORE_MOD_TAP_INTERRUPT` option is enabled, the `SFT_T(KC_A)` key must be held longer than the `TAPPING_TERM` to register the hold action. A quick tap will output `ax` in this case, while a hold will still output a capital `X` (`SHIFT`+`x`).
|
|
||||||
|
|
||||||
However, if the `HOLD_ON_OTHER_KEY_PRESS` option is enabled in addition to `IGNORE_MOD_TAP_INTERRUPT`, the above sequence will again send a capital `X` (`SHIFT`+`x`) even if performed faster than the `TAPPING_TERM`. The difference from the default configuration is that by default the host will receive the key events only after the `SFT_T(KC_A)` key is released, but with the `HOLD_ON_OTHER_KEY_PRESS` option, the host will start receiving key events when the `KC_X` key is pressed.
|
|
||||||
|
|
||||||
For more granular control of this feature, you can add the following to your `config.h`:
|
|
||||||
|
|
||||||
```c
|
|
||||||
#define HOLD_ON_OTHER_KEY_PRESS_PER_KEY
|
|
||||||
```
|
|
||||||
|
|
||||||
?> This option affects *all* dual-role keys.
|
|
||||||
|
|
||||||
You can then add the following function to your keymap:
|
|
||||||
|
|
||||||
```c
|
|
||||||
bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) {
|
|
||||||
switch (keycode) {
|
|
||||||
case SFT_T(KC_SPC):
|
|
||||||
// Do not force the mod-tap key press to be handled as a modifier
|
|
||||||
// if any other key was pressed while the mod-tap key is held down.
|
|
||||||
return false;
|
|
||||||
default:
|
|
||||||
// Force the dual-role key press to be handled as a modifier if any
|
|
||||||
// other key was pressed while the mod-tap key is held down.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that you must return `false` in `get_hold_on_other_key_press` in order to apply `IGNORE_MOD_TAP_INTERRUPT` for a certain mod-tap key.
|
|
||||||
|
|
||||||
?> `IGNORE_MOD_TAP_INTERRUPT[_PER_KEY]` is being progressively phased out to align the (default) behavior and configuration of mod-taps with the rest of dual-role keys.
|
|
||||||
|
|
||||||
## Quick Tap Term
|
## Quick Tap Term
|
||||||
|
|
||||||
When the user holds a key after tapping it, the tapping function is repeated by default, rather than activating the hold function. This allows keeping the ability to auto-repeat the tapping function of a dual-role key. `QUICK_TAP_TERM` enables fine tuning of that ability. If set to `0`, it will remove the auto-repeat ability and activate the hold function instead.
|
When the user holds a key after tapping it, the tapping function is repeated by default, rather than activating the hold function. This allows keeping the ability to auto-repeat the tapping function of a dual-role key. `QUICK_TAP_TERM` enables fine tuning of that ability. If set to `0`, it will remove the auto-repeat ability and activate the hold function instead.
|
||||||
|
@ -129,6 +129,8 @@ The `process_record()` function itself is deceptively simple, but hidden within
|
|||||||
|
|
||||||
* [`void action_exec(keyevent_t event)`](https://github.com/qmk/qmk_firmware/blob/325da02e57fe7374e77b82cb00360ba45167e25c/quantum/action.c#L78-L140)
|
* [`void action_exec(keyevent_t event)`](https://github.com/qmk/qmk_firmware/blob/325da02e57fe7374e77b82cb00360ba45167e25c/quantum/action.c#L78-L140)
|
||||||
* [`void pre_process_record_quantum(keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/325da02e57fe7374e77b82cb00360ba45167e25c/quantum/quantum.c#L204)
|
* [`void pre_process_record_quantum(keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/325da02e57fe7374e77b82cb00360ba45167e25c/quantum/quantum.c#L204)
|
||||||
|
* [`bool pre_process_record_kb(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/27119fa77e8a1b95fff80718d3db4f3e32849298/quantum/quantum.c#L117)
|
||||||
|
* [`bool pre_process_record_user(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/27119fa77e8a1b95fff80718d3db4f3e32849298/quantum/quantum.c#L121)
|
||||||
* [`bool process_combo(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/325da02e57fe7374e77b82cb00360ba45167e25c/quantum/process_keycode/process_combo.c#L521)
|
* [`bool process_combo(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/325da02e57fe7374e77b82cb00360ba45167e25c/quantum/process_keycode/process_combo.c#L521)
|
||||||
* [`void process_record(keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/325da02e57fe7374e77b82cb00360ba45167e25c/quantum/action.c#L254)
|
* [`void process_record(keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/325da02e57fe7374e77b82cb00360ba45167e25c/quantum/action.c#L254)
|
||||||
* [`bool process_record_quantum(keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/325da02e57fe7374e77b82cb00360ba45167e25c/quantum/quantum.c#L224)
|
* [`bool process_record_quantum(keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/325da02e57fe7374e77b82cb00360ba45167e25c/quantum/quantum.c#L224)
|
||||||
|
@ -72,12 +72,12 @@ WS2812_DRIVER = i2c
|
|||||||
|
|
||||||
Configure the hardware via your config.h:
|
Configure the hardware via your config.h:
|
||||||
```c
|
```c
|
||||||
#define WS2812_ADDRESS 0xb0 // default: 0xb0
|
#define WS2812_I2C_ADDRESS 0xB0 // default: 0xB0
|
||||||
#define WS2812_TIMEOUT 100 // default: 100
|
#define WS2812_I2C_TIMEOUT 100 // default: 100
|
||||||
```
|
```
|
||||||
|
|
||||||
### SPI
|
### SPI
|
||||||
Targeting STM32 boards where WS2812 support is offloaded to an SPI hardware device. The advantage is that the use of DMA offloads processing of the WS2812 protocol from the MCU. `RGB_DI_PIN` for this driver is the configured SPI MOSI pin. Due to the nature of repurposing SPI to drive the LEDs, the other SPI pins, MISO and SCK, **must** remain unused. To configure it, add this to your rules.mk:
|
Targeting STM32 boards where WS2812 support is offloaded to an SPI hardware device. The advantage is that the use of DMA offloads processing of the WS2812 protocol from the MCU. `WS2812_DI_PIN` for this driver is the configured SPI MOSI pin. Due to the nature of repurposing SPI to drive the LEDs, the other SPI pins, MISO and SCK, **must** remain unused. To configure it, add this to your rules.mk:
|
||||||
|
|
||||||
```make
|
```make
|
||||||
WS2812_DRIVER = spi
|
WS2812_DRIVER = spi
|
||||||
@ -183,7 +183,7 @@ This can be configured for bitbang, PWM and SPI.
|
|||||||
|
|
||||||
Note: This only applies to STM32 boards.
|
Note: This only applies to STM32 boards.
|
||||||
|
|
||||||
To configure the `RGB_DI_PIN` to open drain configuration add this to your config.h file:
|
To configure the `WS2812_DI_PIN` to open drain configuration add this to your config.h file:
|
||||||
```c
|
```c
|
||||||
#define WS2812_EXTERNAL_PULLUP
|
#define WS2812_EXTERNAL_PULLUP
|
||||||
```
|
```
|
||||||
|
@ -42,7 +42,6 @@
|
|||||||
* [键映射总览](zh-cn/keymap.md)
|
* [键映射总览](zh-cn/keymap.md)
|
||||||
* 开发环境
|
* 开发环境
|
||||||
* [Docker指南](zh-cn/getting_started_docker.md)
|
* [Docker指南](zh-cn/getting_started_docker.md)
|
||||||
* [Vagrant指南](zh-cn/getting_started_vagrant.md)
|
|
||||||
* 刷写(Flashing)
|
* 刷写(Flashing)
|
||||||
* [刷写](zh-cn/flashing.md)
|
* [刷写](zh-cn/flashing.md)
|
||||||
* [刷写ATmega32A (ps2avrgb)](zh-cn/flashing_bootloadhid.md)
|
* [刷写ATmega32A (ps2avrgb)](zh-cn/flashing_bootloadhid.md)
|
||||||
@ -74,6 +73,7 @@
|
|||||||
* [Mod-Tap](zh-cn/mod_tap.md)
|
* [Mod-Tap](zh-cn/mod_tap.md)
|
||||||
* [宏](zh-cn/feature_macros.md)
|
* [宏](zh-cn/feature_macros.md)
|
||||||
* [鼠标键](zh-cn/feature_mouse_keys.md)
|
* [鼠标键](zh-cn/feature_mouse_keys.md)
|
||||||
|
* [Repeat Key](zh-cn/feature_repeat_key.md)
|
||||||
* [Space Cadet Shift](zh-cn/feature_space_cadet.md)
|
* [Space Cadet Shift](zh-cn/feature_space_cadet.md)
|
||||||
* [US ANSI上档键值](zh-cn/keycodes_us_ansi_shifted.md)
|
* [US ANSI上档键值](zh-cn/keycodes_us_ansi_shifted.md)
|
||||||
|
|
||||||
@ -188,5 +188,5 @@
|
|||||||
* [Midi辅助功能](zh-cn/internals/midi_util.md)
|
* [Midi辅助功能](zh-cn/internals/midi_util.md)
|
||||||
* [发送函数](zh-cn/internals/send_functions.md)
|
* [发送函数](zh-cn/internals/send_functions.md)
|
||||||
* [Sysex工具](zh-cn/internals/sysex_tools.md)
|
* [Sysex工具](zh-cn/internals/sysex_tools.md)
|
||||||
|
|
||||||
<!--fromen:20211014-12:00(GMT+8) commit 04cf161aa01fd433b5dae69d9fd31569ed5dca59-->
|
<!--fromen:20211014-12:00(GMT+8) commit 04cf161aa01fd433b5dae69d9fd31569ed5dca59-->
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
# Vagrant快速上手指引
|
|
||||||
|
|
||||||
<!---
|
|
||||||
original document: 0.15.12:docs/getting_started_vagrant.md
|
|
||||||
git diff 0.15.12 HEAD -- docs/getting_started_vagrant.md | cat
|
|
||||||
-->
|
|
||||||
|
|
||||||
本工程包含一份 `Vagrantfile`,可以方便地在不更改你系统环境情况下完成新固件文件的构建工作。这同时也保证了在你拉取该工程代码后的编译环境与也使用Vagrantfile的其它人的一致。当你需要其他人协助你排查遇到的问题时会方便很多。
|
|
||||||
|
|
||||||
## 需求
|
|
||||||
|
|
||||||
本工程中的 `Vagrantfile` 需要安装[Vagrant](https://www.vagrantup.com/)以及可用的虚拟机服务:
|
|
||||||
|
|
||||||
* [VirtualBox](https://www.virtualbox.org/) (5.0.12及以后版本)
|
|
||||||
* 卖点是'最适用于Vagrant的平台'
|
|
||||||
* [VMware Workstation](https://www.vmware.com/products/workstation) 及 [Vagrant VMware插件](https://www.vagrantup.com/vmware)
|
|
||||||
* (付费购买的)VMware插件需要在经过正版授权的VMware Workstation/Fusion上运行
|
|
||||||
* [Docker](https://www.docker.com/)
|
|
||||||
|
|
||||||
安装了Vagrant之后,在安装合适的虚拟机服务后可能需要重启机器。拉取本工程后在工程目录下执行 'vagrant up' 将启动一个包含了所有本工程所需工具的构建环境(虚拟机或是容器)。最后会有一个vagrant启动提示告知你一切正常就绪,否则你也可以参考一下下面的构建文档。
|
|
||||||
|
|
||||||
## 刷写固件
|
|
||||||
|
|
||||||
比较“简单”的方案是在你的宿主系统上借助以下工具刷写固件:
|
|
||||||
|
|
||||||
* [QMK工具箱](https://github.com/qmk/qmk_toolbox) (推荐)
|
|
||||||
* [Teensy Loader](https://www.pjrc.com/teensy/loader.html)
|
|
||||||
|
|
||||||
如果你希望通过命令行进行编程工作,可以在Vagrantfile中取消掉['modifyvm']的注释以允许USB直通到Linux环境,既可以使用dfu-util/dfu-programmer之类的命令行工具进行编程工作,或是安装Teensy的命令行版本。
|
|
||||||
|
|
||||||
## Vagrantfile概览
|
|
||||||
开发环境被配置为运行QMK Docker镜像 `qmkfm/qmk_cli`,不仅让各系统下的功能预期一致,也是我们CI环境的镜像。
|
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
### 为什么我的VirtualBox环境会有问题?
|
|
||||||
VirtualBox 5的某些版本与工程中Vagrantfile中指定的VirtualBox扩展存在兼容问题。如果你遇到了/vagrant挂载不成功的问题,请升级VirtualBox至5.0.12或更高版本。**或者,可以尝试执行如下命令:**
|
|
||||||
|
|
||||||
```console
|
|
||||||
vagrant plugin install vagrant-vbguest
|
|
||||||
```
|
|
||||||
|
|
||||||
### 如何移除一个现有环境?
|
|
||||||
不再需要这个环境了是吗?在本工程目录下的任何位置,执行:
|
|
||||||
|
|
||||||
```console
|
|
||||||
vagrant destroy
|
|
||||||
```
|
|
||||||
|
|
||||||
### 如果我是想直接用Docker呢?
|
|
||||||
想在不使用虚拟机技术的情况下也能使用Vagrant工作流?Vagrangfile已配置为允许绕过运行虚拟机,直接运行容器。通过如下方式执行命令可以强制使用Docker来启动环境:
|
|
||||||
```console
|
|
||||||
vagrant up --provider=docker
|
|
||||||
```
|
|
||||||
|
|
||||||
### 如何访问虚拟机环境而非Docker容器?
|
|
||||||
通过如下方法跳过 `vagrant` 的用户初始化过程以在QMK构建镜像中直接执行:
|
|
||||||
|
|
||||||
```console
|
|
||||||
vagrant ssh -c 'sudo -i'
|
|
||||||
```
|
|
@ -117,8 +117,6 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
在数字及字母键上使用Mod-Tap时推荐启用 `IGNORE_MOD_TAP_INTERRUPT`,以避免在快速按下下一个键时保持功能优先级。参见[忽略Mod Tap中断](zh-cn/tap_hold.md#ignore-mod-tap-interrupt)。
|
|
||||||
|
|
||||||
### 同时改变点击和按住功能
|
### 同时改变点击和按住功能
|
||||||
|
|
||||||
最后一个例子通过 `LT(0,KC_NO)` 实现了点击复制,按住粘贴的功能:
|
最后一个例子通过 `LT(0,KC_NO)` 实现了点击复制,按住粘贴的功能:
|
||||||
|
@ -21,9 +21,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \defgroup hd44780
|
* \file
|
||||||
*
|
*
|
||||||
* HD44780 Character LCD Driver
|
* \defgroup hd44780 HD44780 Character LCD Driver
|
||||||
* \{
|
* \{
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
# if defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F3XX) || defined(STM32F4XX) || defined(STM32L0XX) || defined(GD32VF103)
|
# if defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F3XX) || defined(STM32F4XX) || defined(STM32L0XX) || defined(GD32VF103)
|
||||||
# define APA102_NOPS (100 / (1000000000L / (CPU_CLOCK / 4))) // This calculates how many loops of 4 nops to run to delay 100 ns
|
# define APA102_NOPS (100 / (1000000000L / (CPU_CLOCK / 4))) // This calculates how many loops of 4 nops to run to delay 100 ns
|
||||||
# else
|
# else
|
||||||
# error("APA102_NOPS configuration required")
|
# error APA102_NOPS configuration required
|
||||||
# define APA102_NOPS 0 // this just pleases the compile so the above error is easier to spot
|
# define APA102_NOPS 0 // this just pleases the compile so the above error is easier to spot
|
||||||
# endif
|
# endif
|
||||||
# endif
|
# endif
|
||||||
@ -43,14 +43,14 @@
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define APA102_SEND_BIT(byte, bit) \
|
#define APA102_SEND_BIT(byte, bit) \
|
||||||
do { \
|
do { \
|
||||||
writePin(RGB_DI_PIN, (byte >> bit) & 1); \
|
writePin(APA102_DI_PIN, (byte >> bit) & 1); \
|
||||||
io_wait; \
|
io_wait; \
|
||||||
writePinHigh(RGB_CI_PIN); \
|
writePinHigh(APA102_CI_PIN); \
|
||||||
io_wait; \
|
io_wait; \
|
||||||
writePinLow(RGB_CI_PIN); \
|
writePinLow(APA102_CI_PIN); \
|
||||||
io_wait; \
|
io_wait; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
uint8_t apa102_led_brightness = APA102_DEFAULT_BRIGHTNESS;
|
uint8_t apa102_led_brightness = APA102_DEFAULT_BRIGHTNESS;
|
||||||
@ -77,11 +77,11 @@ void rgblight_call_driver(LED_TYPE *start_led, uint8_t num_leds) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void static apa102_init(void) {
|
void static apa102_init(void) {
|
||||||
setPinOutput(RGB_DI_PIN);
|
setPinOutput(APA102_DI_PIN);
|
||||||
setPinOutput(RGB_CI_PIN);
|
setPinOutput(APA102_CI_PIN);
|
||||||
|
|
||||||
writePinLow(RGB_DI_PIN);
|
writePinLow(APA102_DI_PIN);
|
||||||
writePinLow(RGB_CI_PIN);
|
writePinLow(APA102_CI_PIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apa102_set_brightness(uint8_t brightness) {
|
void apa102_set_brightness(uint8_t brightness) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/* Copyright 2021 Jasper Chan
|
/* Copyright 2021 Jasper Chan
|
||||||
|
* 2023 Huckies <https://github.com/Huckies>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -15,6 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "aw20216.h"
|
#include "aw20216.h"
|
||||||
|
#include "wait.h"
|
||||||
#include "spi_master.h"
|
#include "spi_master.h"
|
||||||
|
|
||||||
/* The AW20216 appears to be somewhat similar to the IS31FL743, although quite
|
/* The AW20216 appears to be somewhat similar to the IS31FL743, although quite
|
||||||
@ -34,6 +36,8 @@
|
|||||||
|
|
||||||
#define AW_REG_CONFIGURATION 0x00 // PG0
|
#define AW_REG_CONFIGURATION 0x00 // PG0
|
||||||
#define AW_REG_GLOBALCURRENT 0x01 // PG0
|
#define AW_REG_GLOBALCURRENT 0x01 // PG0
|
||||||
|
#define AW_REG_RESET 0x2F // PG0
|
||||||
|
#define AW_REG_MIXFUNCTION 0x46 // PG0
|
||||||
|
|
||||||
// Default value of AW_REG_CONFIGURATION
|
// Default value of AW_REG_CONFIGURATION
|
||||||
// D7:D4 = 1011, SWSEL (SW1~SW12 active)
|
// D7:D4 = 1011, SWSEL (SW1~SW12 active)
|
||||||
@ -41,7 +45,10 @@
|
|||||||
// D2:D1 = 00, OSDE (open/short detection enable)
|
// D2:D1 = 00, OSDE (open/short detection enable)
|
||||||
// D0 = 0, CHIPEN (write 1 to enable LEDs when hardware enable pulled high)
|
// D0 = 0, CHIPEN (write 1 to enable LEDs when hardware enable pulled high)
|
||||||
#define AW_CONFIG_DEFAULT 0b10110000
|
#define AW_CONFIG_DEFAULT 0b10110000
|
||||||
|
#define AW_MIXCR_DEFAULT 0b00000000
|
||||||
|
#define AW_RESET_CMD 0xAE
|
||||||
#define AW_CHIPEN 1
|
#define AW_CHIPEN 1
|
||||||
|
#define AW_LPEN (0x01 << 1)
|
||||||
|
|
||||||
#define AW_PWM_REGISTER_COUNT 216
|
#define AW_PWM_REGISTER_COUNT 216
|
||||||
|
|
||||||
@ -94,6 +101,10 @@ static inline bool AW20216_write_register(pin_t cs_pin, uint8_t page, uint8_t re
|
|||||||
return AW20216_write(cs_pin, page, reg, &value, 1);
|
return AW20216_write(cs_pin, page, reg, &value, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AW20216_soft_reset(pin_t cs_pin) {
|
||||||
|
AW20216_write_register(cs_pin, AW_PAGE_FUNCTION, AW_REG_RESET, AW_RESET_CMD);
|
||||||
|
}
|
||||||
|
|
||||||
static void AW20216_init_scaling(pin_t cs_pin) {
|
static void AW20216_init_scaling(pin_t cs_pin) {
|
||||||
// Set constant current to the max, control brightness with PWM
|
// Set constant current to the max, control brightness with PWM
|
||||||
for (uint8_t i = 0; i < AW_PWM_REGISTER_COUNT; i++) {
|
for (uint8_t i = 0; i < AW_PWM_REGISTER_COUNT; i++) {
|
||||||
@ -111,15 +122,23 @@ static inline void AW20216_soft_enable(pin_t cs_pin) {
|
|||||||
AW20216_write_register(cs_pin, AW_PAGE_FUNCTION, AW_REG_CONFIGURATION, AW_CONFIG_DEFAULT | AW_CHIPEN);
|
AW20216_write_register(cs_pin, AW_PAGE_FUNCTION, AW_REG_CONFIGURATION, AW_CONFIG_DEFAULT | AW_CHIPEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void AW20216_auto_lowpower(pin_t cs_pin) {
|
||||||
|
AW20216_write_register(cs_pin, AW_PAGE_FUNCTION, AW_REG_MIXFUNCTION, AW_MIXCR_DEFAULT | AW_LPEN);
|
||||||
|
}
|
||||||
|
|
||||||
void AW20216_init(pin_t cs_pin, pin_t en_pin) {
|
void AW20216_init(pin_t cs_pin, pin_t en_pin) {
|
||||||
setPinOutput(en_pin);
|
setPinOutput(en_pin);
|
||||||
writePinHigh(en_pin);
|
writePinHigh(en_pin);
|
||||||
|
|
||||||
|
AW20216_soft_reset(cs_pin);
|
||||||
|
wait_ms(2);
|
||||||
|
|
||||||
// Drivers should start with all scaling and PWM registers as off
|
// Drivers should start with all scaling and PWM registers as off
|
||||||
AW20216_init_current_limit(cs_pin);
|
AW20216_init_current_limit(cs_pin);
|
||||||
AW20216_init_scaling(cs_pin);
|
AW20216_init_scaling(cs_pin);
|
||||||
|
|
||||||
AW20216_soft_enable(cs_pin);
|
AW20216_soft_enable(cs_pin);
|
||||||
|
AW20216_auto_lowpower(cs_pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||||
|
@ -45,9 +45,7 @@ void IS31FL3218_write_register(uint8_t reg, uint8_t data) {
|
|||||||
|
|
||||||
void IS31FL3218_write_pwm_buffer(uint8_t *pwm_buffer) {
|
void IS31FL3218_write_pwm_buffer(uint8_t *pwm_buffer) {
|
||||||
g_twi_transfer_buffer[0] = ISSI_REG_PWM;
|
g_twi_transfer_buffer[0] = ISSI_REG_PWM;
|
||||||
for (int i = 0; i < 18; i++) {
|
memcpy(g_twi_transfer_buffer + 1, pwm_buffer, 18);
|
||||||
g_twi_transfer_buffer[1 + i] = pwm_buffer[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
i2c_transmit(ISSI_ADDRESS, g_twi_transfer_buffer, 19, ISSI_TIMEOUT);
|
i2c_transmit(ISSI_ADDRESS, g_twi_transfer_buffer, 19, ISSI_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
void IS31FL3218_init(void);
|
void IS31FL3218_init(void);
|
||||||
void IS31FL3218_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
void IS31FL3218_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||||
|
@ -123,9 +123,7 @@ void IS31FL3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
|||||||
// copy the data from i to i+15
|
// copy the data from i to i+15
|
||||||
// device will auto-increment register for data after the first byte
|
// device will auto-increment register for data after the first byte
|
||||||
// thus this sets registers 0x24-0x33, 0x34-0x43, etc. in one transfer
|
// thus this sets registers 0x24-0x33, 0x34-0x43, etc. in one transfer
|
||||||
for (int j = 0; j < 16; j++) {
|
memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
|
||||||
g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
|
|
||||||
}
|
|
||||||
|
|
||||||
#if ISSI_PERSISTENCE > 0
|
#if ISSI_PERSISTENCE > 0
|
||||||
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
|
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
#include "progmem.h"
|
#include "progmem.h"
|
||||||
|
|
||||||
typedef struct is31_led {
|
typedef struct is31_led {
|
||||||
|
@ -111,9 +111,7 @@ void IS31FL3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
|||||||
// copy the data from i to i+15
|
// copy the data from i to i+15
|
||||||
// device will auto-increment register for data after the first byte
|
// device will auto-increment register for data after the first byte
|
||||||
// thus this sets registers 0x24-0x33, 0x34-0x43, etc. in one transfer
|
// thus this sets registers 0x24-0x33, 0x34-0x43, etc. in one transfer
|
||||||
for (int j = 0; j < 16; j++) {
|
memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
|
||||||
g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
|
|
||||||
}
|
|
||||||
|
|
||||||
#if ISSI_PERSISTENCE > 0
|
#if ISSI_PERSISTENCE > 0
|
||||||
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
|
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
#include "progmem.h"
|
#include "progmem.h"
|
||||||
|
|
||||||
typedef struct is31_led {
|
typedef struct is31_led {
|
||||||
|
@ -129,9 +129,7 @@ bool IS31FL3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
|||||||
// Copy the data from i to i+15.
|
// Copy the data from i to i+15.
|
||||||
// Device will auto-increment register for data after the first byte
|
// Device will auto-increment register for data after the first byte
|
||||||
// Thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer.
|
// Thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer.
|
||||||
for (int j = 0; j < 16; j++) {
|
memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
|
||||||
g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
|
|
||||||
}
|
|
||||||
|
|
||||||
#if ISSI_PERSISTENCE > 0
|
#if ISSI_PERSISTENCE > 0
|
||||||
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
|
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
#include "progmem.h"
|
#include "progmem.h"
|
||||||
|
|
||||||
typedef struct is31_led {
|
typedef struct is31_led {
|
||||||
|
@ -77,7 +77,7 @@ uint8_t g_twi_transfer_buffer[20];
|
|||||||
// buffers and the transfers in IS31FL3736_write_pwm_buffer() but it's
|
// buffers and the transfers in IS31FL3736_write_pwm_buffer() but it's
|
||||||
// probably not worth the extra complexity.
|
// probably not worth the extra complexity.
|
||||||
uint8_t g_pwm_buffer[DRIVER_COUNT][192];
|
uint8_t g_pwm_buffer[DRIVER_COUNT][192];
|
||||||
bool g_pwm_buffer_update_required = false;
|
bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
|
||||||
|
|
||||||
uint8_t g_led_control_registers[DRIVER_COUNT][24] = {{0}, {0}};
|
uint8_t g_led_control_registers[DRIVER_COUNT][24] = {{0}, {0}};
|
||||||
bool g_led_control_registers_update_required = false;
|
bool g_led_control_registers_update_required = false;
|
||||||
@ -107,9 +107,7 @@ void IS31FL3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
|||||||
// copy the data from i to i+15
|
// copy the data from i to i+15
|
||||||
// device will auto-increment register for data after the first byte
|
// device will auto-increment register for data after the first byte
|
||||||
// thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer
|
// thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer
|
||||||
for (int j = 0; j < 16; j++) {
|
memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
|
||||||
g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
|
|
||||||
}
|
|
||||||
|
|
||||||
#if ISSI_PERSISTENCE > 0
|
#if ISSI_PERSISTENCE > 0
|
||||||
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
|
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
|
||||||
@ -171,10 +169,10 @@ void IS31FL3736_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
|||||||
if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
|
if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
|
||||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||||
|
|
||||||
g_pwm_buffer[led.driver][led.r] = red;
|
g_pwm_buffer[led.driver][led.r] = red;
|
||||||
g_pwm_buffer[led.driver][led.g] = green;
|
g_pwm_buffer[led.driver][led.g] = green;
|
||||||
g_pwm_buffer[led.driver][led.b] = blue;
|
g_pwm_buffer[led.driver][led.b] = blue;
|
||||||
g_pwm_buffer_update_required = true;
|
g_pwm_buffer_update_required[led.driver] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,9 +230,9 @@ void IS31FL3736_mono_set_brightness(int index, uint8_t value) {
|
|||||||
if (index >= 0 && index < 96) {
|
if (index >= 0 && index < 96) {
|
||||||
// Index in range 0..95 -> A1..A8, B1..B8, etc.
|
// Index in range 0..95 -> A1..A8, B1..B8, etc.
|
||||||
// Map index 0..95 to registers 0x00..0xBE (interleaved)
|
// Map index 0..95 to registers 0x00..0xBE (interleaved)
|
||||||
uint8_t pwm_register = index * 2;
|
uint8_t pwm_register = index * 2;
|
||||||
g_pwm_buffer[0][pwm_register] = value;
|
g_pwm_buffer[0][pwm_register] = value;
|
||||||
g_pwm_buffer_update_required = true;
|
g_pwm_buffer_update_required[0] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,16 +260,15 @@ void IS31FL3736_mono_set_led_control_register(uint8_t index, bool enabled) {
|
|||||||
g_led_control_registers_update_required = true;
|
g_led_control_registers_update_required = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IS31FL3736_update_pwm_buffers(uint8_t addr1, uint8_t addr2) {
|
void IS31FL3736_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||||
if (g_pwm_buffer_update_required) {
|
if (g_pwm_buffer_update_required[index]) {
|
||||||
// Firstly we need to unlock the command register and select PG1
|
// Firstly we need to unlock the command register and select PG1
|
||||||
IS31FL3736_write_register(addr1, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
IS31FL3736_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||||
IS31FL3736_write_register(addr1, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
|
IS31FL3736_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
|
||||||
|
|
||||||
IS31FL3736_write_pwm_buffer(addr1, g_pwm_buffer[0]);
|
IS31FL3736_write_pwm_buffer(addr, g_pwm_buffer[index]);
|
||||||
// IS31FL3736_write_pwm_buffer(addr2, g_pwm_buffer[1]);
|
|
||||||
}
|
}
|
||||||
g_pwm_buffer_update_required = false;
|
g_pwm_buffer_update_required[index] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IS31FL3736_update_led_control_registers(uint8_t addr1, uint8_t addr2) {
|
void IS31FL3736_update_led_control_registers(uint8_t addr1, uint8_t addr2) {
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
#include "progmem.h"
|
#include "progmem.h"
|
||||||
|
|
||||||
// Simple interface option.
|
// Simple interface option.
|
||||||
@ -58,8 +59,8 @@ void IS31FL3736_mono_set_led_control_register(uint8_t index, bool enabled);
|
|||||||
// (eg. from a timer interrupt).
|
// (eg. from a timer interrupt).
|
||||||
// Call this while idle (in between matrix scans).
|
// Call this while idle (in between matrix scans).
|
||||||
// If the buffer is dirty, it will update the driver with the buffer.
|
// If the buffer is dirty, it will update the driver with the buffer.
|
||||||
void IS31FL3736_update_pwm_buffers(uint8_t addr1, uint8_t addr2);
|
void IS31FL3736_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||||
void IS31FL3736_update_led_control_registers(uint8_t addr1, uint8_t addr2);
|
void IS31FL3736_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||||
|
|
||||||
#define PUR_0R 0x00 // No PUR resistor
|
#define PUR_0R 0x00 // No PUR resistor
|
||||||
#define PUR_05KR 0x01 // 0.5k Ohm resistor
|
#define PUR_05KR 0x01 // 0.5k Ohm resistor
|
||||||
|
@ -114,9 +114,7 @@ void IS31FL3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
|||||||
// copy the data from i to i+15
|
// copy the data from i to i+15
|
||||||
// device will auto-increment register for data after the first byte
|
// device will auto-increment register for data after the first byte
|
||||||
// thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer
|
// thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer
|
||||||
for (int j = 0; j < 16; j++) {
|
memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
|
||||||
g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
|
|
||||||
}
|
|
||||||
|
|
||||||
#if ISSI_PERSISTENCE > 0
|
#if ISSI_PERSISTENCE > 0
|
||||||
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
|
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
#include "progmem.h"
|
#include "progmem.h"
|
||||||
|
|
||||||
typedef struct is31_led {
|
typedef struct is31_led {
|
||||||
@ -45,8 +46,8 @@ void IS31FL3737_set_led_control_register(uint8_t index, bool red, bool green, bo
|
|||||||
// (eg. from a timer interrupt).
|
// (eg. from a timer interrupt).
|
||||||
// Call this while idle (in between matrix scans).
|
// Call this while idle (in between matrix scans).
|
||||||
// If the buffer is dirty, it will update the driver with the buffer.
|
// If the buffer is dirty, it will update the driver with the buffer.
|
||||||
void IS31FL3737_update_pwm_buffers(uint8_t addr1, uint8_t addr2);
|
void IS31FL3737_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||||
void IS31FL3737_update_led_control_registers(uint8_t addr1, uint8_t addr2);
|
void IS31FL3737_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||||
|
|
||||||
#define PUR_0R 0x00 // No PUR resistor
|
#define PUR_0R 0x00 // No PUR resistor
|
||||||
#define PUR_05KR 0x01 // 0.5k Ohm resistor in t_NOL
|
#define PUR_05KR 0x01 // 0.5k Ohm resistor in t_NOL
|
||||||
|
@ -104,13 +104,11 @@ void IS31FL3741_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool IS31FL3741_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
bool IS31FL3741_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||||
// unlock the command register and select PG2
|
// Assume PG0 is already selected
|
||||||
IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
|
||||||
IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM0);
|
|
||||||
|
|
||||||
for (int i = 0; i < 342; i += 18) {
|
for (int i = 0; i < 342; i += 18) {
|
||||||
if (i == 180) {
|
if (i == 180) {
|
||||||
// unlock the command register and select PG2
|
// unlock the command register and select PG1
|
||||||
IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||||
IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM1);
|
IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM1);
|
||||||
}
|
}
|
||||||
@ -222,6 +220,10 @@ void IS31FL3741_set_led_control_register(uint8_t index, bool red, bool green, bo
|
|||||||
|
|
||||||
void IS31FL3741_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
void IS31FL3741_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||||
if (g_pwm_buffer_update_required[index]) {
|
if (g_pwm_buffer_update_required[index]) {
|
||||||
|
// unlock the command register and select PG2
|
||||||
|
IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||||
|
IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM0);
|
||||||
|
|
||||||
IS31FL3741_write_pwm_buffer(addr, g_pwm_buffer[index]);
|
IS31FL3741_write_pwm_buffer(addr, g_pwm_buffer[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,7 +246,7 @@ void IS31FL3741_update_led_control_registers(uint8_t addr, uint8_t index) {
|
|||||||
|
|
||||||
// CS1_SW1 to CS30_SW6 are on PG2
|
// CS1_SW1 to CS30_SW6 are on PG2
|
||||||
for (int i = CS1_SW1; i <= CS30_SW6; ++i) {
|
for (int i = CS1_SW1; i <= CS30_SW6; ++i) {
|
||||||
IS31FL3741_write_register(addr, i, g_scaling_registers[0][i]);
|
IS31FL3741_write_register(addr, i, g_scaling_registers[index][i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// unlock the command register and select PG3
|
// unlock the command register and select PG3
|
||||||
@ -253,7 +255,7 @@ void IS31FL3741_update_led_control_registers(uint8_t addr, uint8_t index) {
|
|||||||
|
|
||||||
// CS1_SW7 to CS39_SW9 are on PG3
|
// CS1_SW7 to CS39_SW9 are on PG3
|
||||||
for (int i = CS1_SW7; i <= CS39_SW9; ++i) {
|
for (int i = CS1_SW7; i <= CS39_SW9; ++i) {
|
||||||
IS31FL3741_write_register(addr, i - CS1_SW7, g_scaling_registers[0][i]);
|
IS31FL3741_write_register(addr, i - CS1_SW7, g_scaling_registers[index][i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_scaling_registers_update_required[index] = false;
|
g_scaling_registers_update_required[index] = false;
|
||||||
|
978
drivers/oled/oled_driver.c
Normal file
978
drivers/oled/oled_driver.c
Normal file
@ -0,0 +1,978 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 Ryan Caltabiano <https://github.com/XScorpion2>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(OLED_TRANSPORT_SPI)
|
||||||
|
# include "spi_master.h"
|
||||||
|
#elif defined(OLED_TRANSPORT_I2C)
|
||||||
|
# include "i2c_master.h"
|
||||||
|
#endif
|
||||||
|
#include "oled_driver.h"
|
||||||
|
#include OLED_FONT_H
|
||||||
|
#include "timer.h"
|
||||||
|
#include "print.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include "progmem.h"
|
||||||
|
#include "wait.h"
|
||||||
|
|
||||||
|
// Used commands from spec sheet: https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
|
||||||
|
// for SH1106: https://www.velleman.eu/downloads/29/infosheets/sh1106_datasheet.pdf
|
||||||
|
// for SH1107: https://www.displayfuture.com/Display/datasheet/controller/SH1107.pdf
|
||||||
|
|
||||||
|
// Fundamental Commands
|
||||||
|
#define CONTRAST 0x81
|
||||||
|
#define DISPLAY_ALL_ON 0xA5
|
||||||
|
#define DISPLAY_ALL_ON_RESUME 0xA4
|
||||||
|
#define NORMAL_DISPLAY 0xA6
|
||||||
|
#define INVERT_DISPLAY 0xA7
|
||||||
|
#define DISPLAY_ON 0xAF
|
||||||
|
#define DISPLAY_OFF 0xAE
|
||||||
|
#define NOP 0xE3
|
||||||
|
|
||||||
|
// Scrolling Commands
|
||||||
|
#define ACTIVATE_SCROLL 0x2F
|
||||||
|
#define DEACTIVATE_SCROLL 0x2E
|
||||||
|
#define SCROLL_RIGHT 0x26
|
||||||
|
#define SCROLL_LEFT 0x27
|
||||||
|
#define SCROLL_RIGHT_UP 0x29
|
||||||
|
#define SCROLL_LEFT_UP 0x2A
|
||||||
|
|
||||||
|
// Addressing Setting Commands
|
||||||
|
#define MEMORY_MODE 0x20
|
||||||
|
#define COLUMN_ADDR 0x21
|
||||||
|
#define PAGE_ADDR 0x22
|
||||||
|
#define PAM_SETCOLUMN_LSB 0x00
|
||||||
|
#define PAM_SETCOLUMN_MSB 0x10
|
||||||
|
#define PAM_PAGE_ADDR 0xB0 // 0xb0 -- 0xb7
|
||||||
|
|
||||||
|
// Hardware Configuration Commands
|
||||||
|
#define DISPLAY_START_LINE 0x40
|
||||||
|
#define SEGMENT_REMAP 0xA0
|
||||||
|
#define SEGMENT_REMAP_INV 0xA1
|
||||||
|
#define MULTIPLEX_RATIO 0xA8
|
||||||
|
#define COM_SCAN_INC 0xC0
|
||||||
|
#define COM_SCAN_DEC 0xC8
|
||||||
|
#define DISPLAY_OFFSET 0xD3
|
||||||
|
#define COM_PINS 0xDA
|
||||||
|
#define COM_PINS_SEQ 0x02
|
||||||
|
#define COM_PINS_ALT 0x12
|
||||||
|
#define COM_PINS_SEQ_LR 0x22
|
||||||
|
#define COM_PINS_ALT_LR 0x32
|
||||||
|
|
||||||
|
// Timing & Driving Commands
|
||||||
|
#define DISPLAY_CLOCK 0xD5
|
||||||
|
#define PRE_CHARGE_PERIOD 0xD9
|
||||||
|
#define VCOM_DETECT 0xDB
|
||||||
|
|
||||||
|
// Advance Graphic Commands
|
||||||
|
#define FADE_BLINK 0x23
|
||||||
|
#define ENABLE_FADE 0x20
|
||||||
|
#define ENABLE_BLINK 0x30
|
||||||
|
|
||||||
|
// Charge Pump Commands
|
||||||
|
#define CHARGE_PUMP 0x8D
|
||||||
|
|
||||||
|
// Commands specific to the SH1107 chip
|
||||||
|
#define SH1107_DISPLAY_START_LINE 0xDC
|
||||||
|
#define SH1107_MEMORY_MODE_PAGE 0x20
|
||||||
|
#define SH1107_MEMORY_MODE_VERTICAL 0x21
|
||||||
|
|
||||||
|
// Misc defines
|
||||||
|
#ifndef OLED_BLOCK_COUNT
|
||||||
|
# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8)
|
||||||
|
#endif
|
||||||
|
#ifndef OLED_BLOCK_SIZE
|
||||||
|
# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)
|
||||||
|
#endif
|
||||||
|
// Default display clock
|
||||||
|
#if !defined(OLED_DISPLAY_CLOCK)
|
||||||
|
# define OLED_DISPLAY_CLOCK 0x80
|
||||||
|
#endif
|
||||||
|
// Default VCOMH deselect value
|
||||||
|
#if !defined(OLED_VCOM_DETECT)
|
||||||
|
# define OLED_VCOM_DETECT 0x20
|
||||||
|
#endif
|
||||||
|
#if !defined(OLED_PRE_CHARGE_PERIOD)
|
||||||
|
# define OLED_PRE_CHARGE_PERIOD 0xF1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define OLED_ALL_BLOCKS_MASK (((((OLED_BLOCK_TYPE)1 << (OLED_BLOCK_COUNT - 1)) - 1) << 1) | 1)
|
||||||
|
|
||||||
|
#define OLED_IC_HAS_HORIZONTAL_MODE (OLED_IC == OLED_IC_SSD1306)
|
||||||
|
#define OLED_IC_COM_PINS_ARE_COLUMNS (OLED_IC == OLED_IC_SH1107)
|
||||||
|
|
||||||
|
#ifndef OLED_COM_PIN_COUNT
|
||||||
|
# if OLED_IC == OLED_IC_SSD1306
|
||||||
|
# define OLED_COM_PIN_COUNT 64
|
||||||
|
# elif OLED_IC == OLED_IC_SH1106
|
||||||
|
# define OLED_COM_PIN_COUNT 64
|
||||||
|
# elif OLED_IC == OLED_IC_SH1107
|
||||||
|
# define OLED_COM_PIN_COUNT 128
|
||||||
|
# else
|
||||||
|
# error Invalid OLED_IC value
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef OLED_COM_PIN_OFFSET
|
||||||
|
# define OLED_COM_PIN_OFFSET 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// i2c defines
|
||||||
|
#define I2C_CMD 0x00
|
||||||
|
#define I2C_DATA 0x40
|
||||||
|
|
||||||
|
#define HAS_FLAGS(bits, flags) ((bits & flags) == flags)
|
||||||
|
|
||||||
|
// Display buffer's is the same as the OLED memory layout
|
||||||
|
// this is so we don't end up with rounding errors with
|
||||||
|
// parts of the display unusable or don't get cleared correctly
|
||||||
|
// and also allows for drawing & inverting
|
||||||
|
uint8_t oled_buffer[OLED_MATRIX_SIZE];
|
||||||
|
uint8_t * oled_cursor;
|
||||||
|
OLED_BLOCK_TYPE oled_dirty = 0;
|
||||||
|
bool oled_initialized = false;
|
||||||
|
bool oled_active = false;
|
||||||
|
bool oled_scrolling = false;
|
||||||
|
bool oled_inverted = false;
|
||||||
|
uint8_t oled_brightness = OLED_BRIGHTNESS;
|
||||||
|
oled_rotation_t oled_rotation = 0;
|
||||||
|
uint8_t oled_rotation_width = 0;
|
||||||
|
uint8_t oled_scroll_speed = 0; // this holds the speed after being remapped to ssd1306 internal values
|
||||||
|
uint8_t oled_scroll_start = 0;
|
||||||
|
uint8_t oled_scroll_end = 7;
|
||||||
|
#if OLED_TIMEOUT > 0
|
||||||
|
uint32_t oled_timeout;
|
||||||
|
#endif
|
||||||
|
#if OLED_SCROLL_TIMEOUT > 0
|
||||||
|
uint32_t oled_scroll_timeout;
|
||||||
|
#endif
|
||||||
|
#if OLED_UPDATE_INTERVAL > 0
|
||||||
|
uint16_t oled_update_timeout;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(OLED_TRANSPORT_SPI)
|
||||||
|
# ifndef OLED_DC_PIN
|
||||||
|
# error "The OLED driver in SPI needs a D/C pin defined"
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_CS_PIN
|
||||||
|
# error "The OLED driver in SPI needs a CS pin defined"
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_SPI_MODE
|
||||||
|
# define OLED_SPI_MODE 3
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_SPI_DIVISOR
|
||||||
|
# define OLED_SPI_DIVISOR 2
|
||||||
|
# endif
|
||||||
|
#elif defined(OLED_TRANSPORT_I2C)
|
||||||
|
# if !defined(OLED_DISPLAY_ADDRESS)
|
||||||
|
# define OLED_DISPLAY_ADDRESS 0x3C
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Transmit/Write Funcs.
|
||||||
|
__attribute__((weak)) bool oled_send_cmd(const uint8_t *data, uint16_t size) {
|
||||||
|
#if defined(OLED_TRANSPORT_SPI)
|
||||||
|
if (!spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Command Mode
|
||||||
|
writePinLow(OLED_DC_PIN);
|
||||||
|
// Send the commands
|
||||||
|
if (spi_transmit(&data[1], size - 1) != SPI_STATUS_SUCCESS) {
|
||||||
|
spi_stop();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
spi_stop();
|
||||||
|
return true;
|
||||||
|
#elif defined(OLED_TRANSPORT_I2C)
|
||||||
|
i2c_status_t status = i2c_transmit((OLED_DISPLAY_ADDRESS << 1), data, size, OLED_I2C_TIMEOUT);
|
||||||
|
|
||||||
|
return (status == I2C_STATUS_SUCCESS);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) bool oled_send_cmd_P(const uint8_t *data, uint16_t size) {
|
||||||
|
#if defined(__AVR__)
|
||||||
|
# if defined(OLED_TRANSPORT_SPI)
|
||||||
|
if (!spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
spi_status_t status = SPI_STATUS_SUCCESS;
|
||||||
|
// Command Mode
|
||||||
|
writePinLow(OLED_DC_PIN);
|
||||||
|
// Send the commands
|
||||||
|
for (uint16_t i = 1; i < size && status >= 0; i++) {
|
||||||
|
status = spi_write(pgm_read_byte((const char *)&data[i]));
|
||||||
|
}
|
||||||
|
spi_stop();
|
||||||
|
return (status >= 0);
|
||||||
|
# elif defined(OLED_TRANSPORT_I2C)
|
||||||
|
i2c_status_t status = i2c_start((OLED_DISPLAY_ADDRESS << 1) | I2C_WRITE, OLED_I2C_TIMEOUT);
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < size && status >= 0; i++) {
|
||||||
|
status = i2c_write(pgm_read_byte((const char *)data++), OLED_I2C_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_stop();
|
||||||
|
|
||||||
|
return (status == I2C_STATUS_SUCCESS);
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
return oled_send_cmd(data, size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) bool oled_send_data(const uint8_t *data, uint16_t size) {
|
||||||
|
#if defined(OLED_TRANSPORT_SPI)
|
||||||
|
if (!spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Data Mode
|
||||||
|
writePinHigh(OLED_DC_PIN);
|
||||||
|
// Send the commands
|
||||||
|
if (spi_transmit(data, size) != SPI_STATUS_SUCCESS) {
|
||||||
|
spi_stop();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
spi_stop();
|
||||||
|
return true;
|
||||||
|
#elif defined(OLED_TRANSPORT_I2C)
|
||||||
|
i2c_status_t status = i2c_writeReg((OLED_DISPLAY_ADDRESS << 1), I2C_DATA, data, size, OLED_I2C_TIMEOUT);
|
||||||
|
return (status == I2C_STATUS_SUCCESS);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) void oled_driver_init(void) {
|
||||||
|
#if defined(OLED_TRANSPORT_SPI)
|
||||||
|
spi_init();
|
||||||
|
setPinOutput(OLED_CS_PIN);
|
||||||
|
writePinHigh(OLED_CS_PIN);
|
||||||
|
|
||||||
|
setPinOutput(OLED_DC_PIN);
|
||||||
|
writePinLow(OLED_DC_PIN);
|
||||||
|
# ifdef OLED_RST_PIN
|
||||||
|
/* Reset device */
|
||||||
|
setPinOutput(OLED_RST_PIN);
|
||||||
|
writePinLow(OLED_RST_PIN);
|
||||||
|
wait_ms(20);
|
||||||
|
writePinHigh(OLED_RST_PIN);
|
||||||
|
wait_ms(20);
|
||||||
|
# endif
|
||||||
|
#elif defined(OLED_TRANSPORT_I2C)
|
||||||
|
i2c_init();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flips the rendering bits for a character at the current cursor position
|
||||||
|
static void InvertCharacter(uint8_t *cursor) {
|
||||||
|
const uint8_t *end = cursor + OLED_FONT_WIDTH;
|
||||||
|
while (cursor < end) {
|
||||||
|
*cursor = ~(*cursor);
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool oled_init(oled_rotation_t rotation) {
|
||||||
|
#if defined(USE_I2C) && defined(SPLIT_KEYBOARD) && defined(OLED_TRANSPORT_I2C)
|
||||||
|
if (!is_keyboard_master()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
oled_rotation = oled_init_user(oled_init_kb(rotation));
|
||||||
|
if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
|
||||||
|
oled_rotation_width = OLED_DISPLAY_WIDTH;
|
||||||
|
} else {
|
||||||
|
oled_rotation_width = OLED_DISPLAY_HEIGHT;
|
||||||
|
}
|
||||||
|
oled_driver_init();
|
||||||
|
|
||||||
|
static const uint8_t PROGMEM display_setup1[] = {
|
||||||
|
I2C_CMD,
|
||||||
|
DISPLAY_OFF,
|
||||||
|
DISPLAY_CLOCK,
|
||||||
|
OLED_DISPLAY_CLOCK,
|
||||||
|
MULTIPLEX_RATIO,
|
||||||
|
#if OLED_IC_COM_PINS_ARE_COLUMNS
|
||||||
|
OLED_DISPLAY_WIDTH - 1,
|
||||||
|
#else
|
||||||
|
OLED_DISPLAY_HEIGHT - 1,
|
||||||
|
#endif
|
||||||
|
#if OLED_IC == OLED_IC_SH1107
|
||||||
|
SH1107_DISPLAY_START_LINE,
|
||||||
|
0x00,
|
||||||
|
#else
|
||||||
|
DISPLAY_START_LINE | 0x00,
|
||||||
|
#endif
|
||||||
|
CHARGE_PUMP,
|
||||||
|
0x14,
|
||||||
|
#if OLED_IC_HAS_HORIZONTAL_MODE
|
||||||
|
// MEMORY_MODE is unsupported on SH1106 (Page Addressing only)
|
||||||
|
MEMORY_MODE,
|
||||||
|
0x00, // Horizontal addressing mode
|
||||||
|
#elif OLED_IC == OLED_IC_SH1107
|
||||||
|
// Page addressing mode
|
||||||
|
SH1107_MEMORY_MODE_PAGE,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
if (!oled_send_cmd_P(display_setup1, ARRAY_SIZE(display_setup1))) {
|
||||||
|
print("oled_init cmd set 1 failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_180)) {
|
||||||
|
static const uint8_t PROGMEM display_normal[] = {
|
||||||
|
I2C_CMD, SEGMENT_REMAP_INV, COM_SCAN_DEC, DISPLAY_OFFSET, OLED_COM_PIN_OFFSET,
|
||||||
|
};
|
||||||
|
if (!oled_send_cmd_P(display_normal, ARRAY_SIZE(display_normal))) {
|
||||||
|
print("oled_init cmd normal rotation failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
static const uint8_t PROGMEM display_flipped[] = {
|
||||||
|
I2C_CMD, SEGMENT_REMAP, COM_SCAN_INC, DISPLAY_OFFSET, (OLED_COM_PIN_COUNT - OLED_COM_PIN_OFFSET) % OLED_COM_PIN_COUNT,
|
||||||
|
};
|
||||||
|
if (!oled_send_cmd_P(display_flipped, ARRAY_SIZE(display_flipped))) {
|
||||||
|
print("display_flipped failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint8_t PROGMEM display_setup2[] = {I2C_CMD, COM_PINS, OLED_COM_PINS, CONTRAST, OLED_BRIGHTNESS, PRE_CHARGE_PERIOD, OLED_PRE_CHARGE_PERIOD, VCOM_DETECT, OLED_VCOM_DETECT, DISPLAY_ALL_ON_RESUME, NORMAL_DISPLAY, DEACTIVATE_SCROLL, DISPLAY_ON};
|
||||||
|
if (!oled_send_cmd_P(display_setup2, ARRAY_SIZE(display_setup2))) {
|
||||||
|
print("display_setup2 failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if OLED_TIMEOUT > 0
|
||||||
|
oled_timeout = timer_read32() + OLED_TIMEOUT;
|
||||||
|
#endif
|
||||||
|
#if OLED_SCROLL_TIMEOUT > 0
|
||||||
|
oled_scroll_timeout = timer_read32() + OLED_SCROLL_TIMEOUT;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
oled_clear();
|
||||||
|
oled_initialized = true;
|
||||||
|
oled_active = true;
|
||||||
|
oled_scrolling = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) oled_rotation_t oled_init_kb(oled_rotation_t rotation) {
|
||||||
|
return rotation;
|
||||||
|
}
|
||||||
|
__attribute__((weak)) oled_rotation_t oled_init_user(oled_rotation_t rotation) {
|
||||||
|
return rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void oled_clear(void) {
|
||||||
|
memset(oled_buffer, 0, sizeof(oled_buffer));
|
||||||
|
oled_cursor = &oled_buffer[0];
|
||||||
|
oled_dirty = OLED_ALL_BLOCKS_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void calc_bounds(uint8_t update_start, uint8_t *cmd_array) {
|
||||||
|
// Calculate commands to set memory addressing bounds.
|
||||||
|
uint8_t start_page = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_WIDTH;
|
||||||
|
uint8_t start_column = OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_WIDTH;
|
||||||
|
#if !OLED_IC_HAS_HORIZONTAL_MODE
|
||||||
|
// Commands for Page Addressing Mode. Sets starting page and column; has no end bound.
|
||||||
|
// Column value must be split into high and low nybble and sent as two commands.
|
||||||
|
cmd_array[0] = PAM_PAGE_ADDR | start_page;
|
||||||
|
cmd_array[1] = PAM_SETCOLUMN_LSB | ((OLED_COLUMN_OFFSET + start_column) & 0x0f);
|
||||||
|
cmd_array[2] = PAM_SETCOLUMN_MSB | ((OLED_COLUMN_OFFSET + start_column) >> 4 & 0x0f);
|
||||||
|
#else
|
||||||
|
// Commands for use in Horizontal Addressing mode.
|
||||||
|
cmd_array[1] = start_column + OLED_COLUMN_OFFSET;
|
||||||
|
cmd_array[4] = start_page;
|
||||||
|
cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) % OLED_DISPLAY_WIDTH + cmd_array[1];
|
||||||
|
cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) / OLED_DISPLAY_WIDTH - 1 + cmd_array[4];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void calc_bounds_90(uint8_t update_start, uint8_t *cmd_array) {
|
||||||
|
// Block numbering starts from the bottom left corner, going up and then to
|
||||||
|
// the right. The controller needs the page and column numbers for the top
|
||||||
|
// left and bottom right corners of that block.
|
||||||
|
|
||||||
|
// Total number of pages across the screen height.
|
||||||
|
const uint8_t height_in_pages = OLED_DISPLAY_HEIGHT / 8;
|
||||||
|
|
||||||
|
// Difference of starting page numbers for adjacent blocks; may be 0 if
|
||||||
|
// blocks are large enough to occupy one or more whole 8px columns.
|
||||||
|
const uint8_t page_inc_per_block = OLED_BLOCK_SIZE % OLED_DISPLAY_HEIGHT / 8;
|
||||||
|
|
||||||
|
// Top page number for a block which is at the bottom edge of the screen.
|
||||||
|
const uint8_t bottom_block_top_page = (height_in_pages - page_inc_per_block) % height_in_pages;
|
||||||
|
|
||||||
|
#if !OLED_IC_HAS_HORIZONTAL_MODE
|
||||||
|
// Only the Page Addressing Mode is supported
|
||||||
|
uint8_t start_page = bottom_block_top_page - (OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_HEIGHT / 8);
|
||||||
|
uint8_t start_column = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_HEIGHT * 8;
|
||||||
|
cmd_array[0] = PAM_PAGE_ADDR | start_page;
|
||||||
|
cmd_array[1] = PAM_SETCOLUMN_LSB | ((OLED_COLUMN_OFFSET + start_column) & 0x0f);
|
||||||
|
cmd_array[2] = PAM_SETCOLUMN_MSB | ((OLED_COLUMN_OFFSET + start_column) >> 4 & 0x0f);
|
||||||
|
#else
|
||||||
|
cmd_array[1] = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_HEIGHT * 8 + OLED_COLUMN_OFFSET;
|
||||||
|
cmd_array[4] = bottom_block_top_page - (OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_HEIGHT / 8);
|
||||||
|
cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) / OLED_DISPLAY_HEIGHT * 8 - 1 + cmd_array[1];
|
||||||
|
cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) % OLED_DISPLAY_HEIGHT / 8 + cmd_array[4];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t crot(uint8_t a, int8_t n) {
|
||||||
|
const uint8_t mask = 0x7;
|
||||||
|
n &= mask;
|
||||||
|
return a << n | a >> (-n & mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rotate_90(const uint8_t *src, uint8_t *dest) {
|
||||||
|
for (uint8_t i = 0, shift = 7; i < 8; ++i, --shift) {
|
||||||
|
uint8_t selector = (1 << i);
|
||||||
|
for (uint8_t j = 0; j < 8; ++j) {
|
||||||
|
dest[i] |= crot(src[j] & selector, shift - (int8_t)j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void oled_render(void) {
|
||||||
|
// Do we have work to do?
|
||||||
|
oled_dirty &= OLED_ALL_BLOCKS_MASK;
|
||||||
|
if (!oled_dirty || !oled_initialized || oled_scrolling) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn on display if it is off
|
||||||
|
oled_on();
|
||||||
|
|
||||||
|
uint8_t update_start = 0;
|
||||||
|
uint8_t num_processed = 0;
|
||||||
|
while (oled_dirty && num_processed++ < OLED_UPDATE_PROCESS_LIMIT) { // render all dirty blocks (up to the configured limit)
|
||||||
|
// Find next dirty block
|
||||||
|
while (!(oled_dirty & ((OLED_BLOCK_TYPE)1 << update_start))) {
|
||||||
|
++update_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set column & page position
|
||||||
|
#if OLED_IC_HAS_HORIZONTAL_MODE
|
||||||
|
static uint8_t display_start[] = {I2C_CMD, COLUMN_ADDR, 0, OLED_DISPLAY_WIDTH - 1, PAGE_ADDR, 0, OLED_DISPLAY_HEIGHT / 8 - 1};
|
||||||
|
#else
|
||||||
|
static uint8_t display_start[] = {I2C_CMD, PAM_PAGE_ADDR, PAM_SETCOLUMN_LSB, PAM_SETCOLUMN_MSB};
|
||||||
|
#endif
|
||||||
|
if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
|
||||||
|
calc_bounds(update_start, &display_start[1]); // Offset from I2C_CMD byte at the start
|
||||||
|
} else {
|
||||||
|
calc_bounds_90(update_start, &display_start[1]); // Offset from I2C_CMD byte at the start
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send column & page position
|
||||||
|
if (!oled_send_cmd(display_start, ARRAY_SIZE(display_start))) {
|
||||||
|
print("oled_render offset command failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
|
||||||
|
// Send render data chunk as is
|
||||||
|
if (!oled_send_data(&oled_buffer[OLED_BLOCK_SIZE * update_start], OLED_BLOCK_SIZE)) {
|
||||||
|
print("oled_render data failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Rotate the render chunks
|
||||||
|
const static uint8_t source_map[] = OLED_SOURCE_MAP;
|
||||||
|
const static uint8_t target_map[] = OLED_TARGET_MAP;
|
||||||
|
|
||||||
|
static uint8_t temp_buffer[OLED_BLOCK_SIZE];
|
||||||
|
memset(temp_buffer, 0, sizeof(temp_buffer));
|
||||||
|
for (uint8_t i = 0; i < sizeof(source_map); ++i) {
|
||||||
|
rotate_90(&oled_buffer[OLED_BLOCK_SIZE * update_start + source_map[i]], &temp_buffer[target_map[i]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if OLED_IC_HAS_HORIZONTAL_MODE
|
||||||
|
// Send render data chunk after rotating
|
||||||
|
if (!oled_send_data(&temp_buffer[0], OLED_BLOCK_SIZE)) {
|
||||||
|
print("oled_render90 data failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// For SH1106 or SH1107 the data chunk must be split into separate pieces for each page
|
||||||
|
const uint8_t columns_in_block = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) / OLED_DISPLAY_HEIGHT * 8;
|
||||||
|
const uint8_t num_pages = OLED_BLOCK_SIZE / columns_in_block;
|
||||||
|
for (uint8_t i = 0; i < num_pages; ++i) {
|
||||||
|
// Send column & page position for all pages except the first one
|
||||||
|
if (i > 0) {
|
||||||
|
display_start[1]++;
|
||||||
|
if (!oled_send_cmd(display_start, ARRAY_SIZE(display_start))) {
|
||||||
|
print("oled_render offset command failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Send data for the page
|
||||||
|
if (!oled_send_data(&temp_buffer[columns_in_block * i], columns_in_block)) {
|
||||||
|
print("oled_render90 data failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear dirty flag of just rendered block
|
||||||
|
oled_dirty &= ~((OLED_BLOCK_TYPE)1 << update_start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void oled_set_cursor(uint8_t col, uint8_t line) {
|
||||||
|
uint16_t index = line * oled_rotation_width + col * OLED_FONT_WIDTH;
|
||||||
|
|
||||||
|
// Out of bounds?
|
||||||
|
if (index >= OLED_MATRIX_SIZE) {
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
oled_cursor = &oled_buffer[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void oled_advance_page(bool clearPageRemainder) {
|
||||||
|
uint16_t index = oled_cursor - &oled_buffer[0];
|
||||||
|
uint8_t remaining = oled_rotation_width - (index % oled_rotation_width);
|
||||||
|
|
||||||
|
if (clearPageRemainder) {
|
||||||
|
// Remaining Char count
|
||||||
|
remaining = remaining / OLED_FONT_WIDTH;
|
||||||
|
|
||||||
|
// Write empty character until next line
|
||||||
|
while (remaining--)
|
||||||
|
oled_write_char(' ', false);
|
||||||
|
} else {
|
||||||
|
// Next page index out of bounds?
|
||||||
|
if (index + remaining >= OLED_MATRIX_SIZE) {
|
||||||
|
index = 0;
|
||||||
|
remaining = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
oled_cursor = &oled_buffer[index + remaining];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void oled_advance_char(void) {
|
||||||
|
uint16_t nextIndex = oled_cursor - &oled_buffer[0] + OLED_FONT_WIDTH;
|
||||||
|
uint8_t remainingSpace = oled_rotation_width - (nextIndex % oled_rotation_width);
|
||||||
|
|
||||||
|
// Do we have enough space on the current line for the next character
|
||||||
|
if (remainingSpace < OLED_FONT_WIDTH) {
|
||||||
|
nextIndex += remainingSpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Did we go out of bounds
|
||||||
|
if (nextIndex >= OLED_MATRIX_SIZE) {
|
||||||
|
nextIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update cursor position
|
||||||
|
oled_cursor = &oled_buffer[nextIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main handler that writes character data to the display buffer
|
||||||
|
void oled_write_char(const char data, bool invert) {
|
||||||
|
// Advance to the next line if newline
|
||||||
|
if (data == '\n') {
|
||||||
|
// Old source wrote ' ' until end of line...
|
||||||
|
oled_advance_page(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data == '\r') {
|
||||||
|
oled_advance_page(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy the current render buffer to check for dirty after
|
||||||
|
static uint8_t oled_temp_buffer[OLED_FONT_WIDTH];
|
||||||
|
memcpy(&oled_temp_buffer, oled_cursor, OLED_FONT_WIDTH);
|
||||||
|
|
||||||
|
_Static_assert(sizeof(font) >= ((OLED_FONT_END + 1 - OLED_FONT_START) * OLED_FONT_WIDTH), "OLED_FONT_END references outside array");
|
||||||
|
|
||||||
|
// set the reder buffer data
|
||||||
|
uint8_t cast_data = (uint8_t)data; // font based on unsigned type for index
|
||||||
|
if (cast_data < OLED_FONT_START || cast_data > OLED_FONT_END) {
|
||||||
|
memset(oled_cursor, 0x00, OLED_FONT_WIDTH);
|
||||||
|
} else {
|
||||||
|
const uint8_t *glyph = &font[(cast_data - OLED_FONT_START) * OLED_FONT_WIDTH];
|
||||||
|
memcpy_P(oled_cursor, glyph, OLED_FONT_WIDTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invert if needed
|
||||||
|
if (invert) {
|
||||||
|
InvertCharacter(oled_cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dirty check
|
||||||
|
if (memcmp(&oled_temp_buffer, oled_cursor, OLED_FONT_WIDTH)) {
|
||||||
|
uint16_t index = oled_cursor - &oled_buffer[0];
|
||||||
|
oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE));
|
||||||
|
// Edgecase check if the written data spans the 2 chunks
|
||||||
|
oled_dirty |= ((OLED_BLOCK_TYPE)1 << ((index + OLED_FONT_WIDTH - 1) / OLED_BLOCK_SIZE));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally move to the next char
|
||||||
|
oled_advance_char();
|
||||||
|
}
|
||||||
|
|
||||||
|
void oled_write(const char *data, bool invert) {
|
||||||
|
const char *end = data + strlen(data);
|
||||||
|
while (data < end) {
|
||||||
|
oled_write_char(*data, invert);
|
||||||
|
data++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void oled_write_ln(const char *data, bool invert) {
|
||||||
|
oled_write(data, invert);
|
||||||
|
oled_advance_page(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void oled_pan(bool left) {
|
||||||
|
uint16_t i = 0;
|
||||||
|
for (uint16_t y = 0; y < OLED_DISPLAY_HEIGHT / 8; y++) {
|
||||||
|
if (left) {
|
||||||
|
for (uint16_t x = 0; x < OLED_DISPLAY_WIDTH - 1; x++) {
|
||||||
|
i = y * OLED_DISPLAY_WIDTH + x;
|
||||||
|
oled_buffer[i] = oled_buffer[i + 1];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (uint16_t x = OLED_DISPLAY_WIDTH - 1; x > 0; x--) {
|
||||||
|
i = y * OLED_DISPLAY_WIDTH + x;
|
||||||
|
oled_buffer[i] = oled_buffer[i - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oled_dirty = OLED_ALL_BLOCKS_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
oled_buffer_reader_t oled_read_raw(uint16_t start_index) {
|
||||||
|
if (start_index > OLED_MATRIX_SIZE) start_index = OLED_MATRIX_SIZE;
|
||||||
|
oled_buffer_reader_t ret_reader;
|
||||||
|
ret_reader.current_element = &oled_buffer[start_index];
|
||||||
|
ret_reader.remaining_element_count = OLED_MATRIX_SIZE - start_index;
|
||||||
|
return ret_reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
void oled_write_raw_byte(const char data, uint16_t index) {
|
||||||
|
if (index > OLED_MATRIX_SIZE) index = OLED_MATRIX_SIZE;
|
||||||
|
if (oled_buffer[index] == data) return;
|
||||||
|
oled_buffer[index] = data;
|
||||||
|
oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE));
|
||||||
|
}
|
||||||
|
|
||||||
|
void oled_write_raw(const char *data, uint16_t size) {
|
||||||
|
uint16_t cursor_start_index = oled_cursor - &oled_buffer[0];
|
||||||
|
if ((size + cursor_start_index) > OLED_MATRIX_SIZE) size = OLED_MATRIX_SIZE - cursor_start_index;
|
||||||
|
for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) {
|
||||||
|
uint8_t c = *data++;
|
||||||
|
if (oled_buffer[i] == c) continue;
|
||||||
|
oled_buffer[i] = c;
|
||||||
|
oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void oled_write_pixel(uint8_t x, uint8_t y, bool on) {
|
||||||
|
if (x >= oled_rotation_width) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint16_t index = x + (y / 8) * oled_rotation_width;
|
||||||
|
if (index >= OLED_MATRIX_SIZE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t data = oled_buffer[index];
|
||||||
|
if (on) {
|
||||||
|
data |= (1 << (y % 8));
|
||||||
|
} else {
|
||||||
|
data &= ~(1 << (y % 8));
|
||||||
|
}
|
||||||
|
if (oled_buffer[index] != data) {
|
||||||
|
oled_buffer[index] = data;
|
||||||
|
oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__AVR__)
|
||||||
|
void oled_write_P(const char *data, bool invert) {
|
||||||
|
uint8_t c = pgm_read_byte(data);
|
||||||
|
while (c != 0) {
|
||||||
|
oled_write_char(c, invert);
|
||||||
|
c = pgm_read_byte(++data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void oled_write_ln_P(const char *data, bool invert) {
|
||||||
|
oled_write_P(data, invert);
|
||||||
|
oled_advance_page(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void oled_write_raw_P(const char *data, uint16_t size) {
|
||||||
|
uint16_t cursor_start_index = oled_cursor - &oled_buffer[0];
|
||||||
|
if ((size + cursor_start_index) > OLED_MATRIX_SIZE) size = OLED_MATRIX_SIZE - cursor_start_index;
|
||||||
|
for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) {
|
||||||
|
uint8_t c = pgm_read_byte(data++);
|
||||||
|
if (oled_buffer[i] == c) continue;
|
||||||
|
oled_buffer[i] = c;
|
||||||
|
oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // defined(__AVR__)
|
||||||
|
|
||||||
|
bool oled_on(void) {
|
||||||
|
if (!oled_initialized) {
|
||||||
|
return oled_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if OLED_TIMEOUT > 0
|
||||||
|
oled_timeout = timer_read32() + OLED_TIMEOUT;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const uint8_t PROGMEM display_on[] =
|
||||||
|
#ifdef OLED_FADE_OUT
|
||||||
|
{I2C_CMD, FADE_BLINK, 0x00};
|
||||||
|
#else
|
||||||
|
{I2C_CMD, DISPLAY_ON};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!oled_active) {
|
||||||
|
if (!oled_send_cmd_P(display_on, ARRAY_SIZE(display_on))) {
|
||||||
|
print("oled_on cmd failed\n");
|
||||||
|
return oled_active;
|
||||||
|
}
|
||||||
|
oled_active = true;
|
||||||
|
}
|
||||||
|
return oled_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool oled_off(void) {
|
||||||
|
if (!oled_initialized) {
|
||||||
|
return !oled_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint8_t PROGMEM display_off[] =
|
||||||
|
#ifdef OLED_FADE_OUT
|
||||||
|
{I2C_CMD, FADE_BLINK, ENABLE_FADE | OLED_FADE_OUT_INTERVAL};
|
||||||
|
#else
|
||||||
|
{I2C_CMD, DISPLAY_OFF};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (oled_active) {
|
||||||
|
if (!oled_send_cmd_P(display_off, ARRAY_SIZE(display_off))) {
|
||||||
|
print("oled_off cmd failed\n");
|
||||||
|
return oled_active;
|
||||||
|
}
|
||||||
|
oled_active = false;
|
||||||
|
}
|
||||||
|
return !oled_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_oled_on(void) {
|
||||||
|
return oled_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t oled_set_brightness(uint8_t level) {
|
||||||
|
if (!oled_initialized) {
|
||||||
|
return oled_brightness;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t set_contrast[] = {I2C_CMD, CONTRAST, level};
|
||||||
|
if (oled_brightness != level) {
|
||||||
|
if (!oled_send_cmd(set_contrast, ARRAY_SIZE(set_contrast))) {
|
||||||
|
print("set_brightness cmd failed\n");
|
||||||
|
return oled_brightness;
|
||||||
|
}
|
||||||
|
oled_brightness = level;
|
||||||
|
}
|
||||||
|
return oled_brightness;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t oled_get_brightness(void) {
|
||||||
|
return oled_brightness;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the specific 8 lines rows of the screen to scroll.
|
||||||
|
// 0 is the default for start, and 7 for end, which is the entire
|
||||||
|
// height of the screen. For 128x32 screens, rows 4-7 are not used.
|
||||||
|
void oled_scroll_set_area(uint8_t start_line, uint8_t end_line) {
|
||||||
|
oled_scroll_start = start_line;
|
||||||
|
oled_scroll_end = end_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
void oled_scroll_set_speed(uint8_t speed) {
|
||||||
|
// Sets the speed for scrolling... does not take effect
|
||||||
|
// until scrolling is either started or restarted
|
||||||
|
// the ssd1306 supports 8 speeds
|
||||||
|
// FrameRate2 speed = 7
|
||||||
|
// FrameRate3 speed = 4
|
||||||
|
// FrameRate4 speed = 5
|
||||||
|
// FrameRate5 speed = 0
|
||||||
|
// FrameRate25 speed = 6
|
||||||
|
// FrameRate64 speed = 1
|
||||||
|
// FrameRate128 speed = 2
|
||||||
|
// FrameRate256 speed = 3
|
||||||
|
// for ease of use these are remaped here to be in order
|
||||||
|
static const uint8_t scroll_remap[8] = {7, 4, 5, 0, 6, 1, 2, 3};
|
||||||
|
oled_scroll_speed = scroll_remap[speed];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool oled_scroll_right(void) {
|
||||||
|
if (!oled_initialized) {
|
||||||
|
return oled_scrolling;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dont enable scrolling if we need to update the display
|
||||||
|
// This prevents scrolling of bad data from starting the scroll too early after init
|
||||||
|
if (!oled_dirty && !oled_scrolling) {
|
||||||
|
uint8_t display_scroll_right[] = {I2C_CMD, SCROLL_RIGHT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL};
|
||||||
|
if (!oled_send_cmd(display_scroll_right, ARRAY_SIZE(display_scroll_right))) {
|
||||||
|
print("oled_scroll_right cmd failed\n");
|
||||||
|
return oled_scrolling;
|
||||||
|
}
|
||||||
|
oled_scrolling = true;
|
||||||
|
}
|
||||||
|
return oled_scrolling;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool oled_scroll_left(void) {
|
||||||
|
if (!oled_initialized) {
|
||||||
|
return oled_scrolling;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dont enable scrolling if we need to update the display
|
||||||
|
// This prevents scrolling of bad data from starting the scroll too early after init
|
||||||
|
if (!oled_dirty && !oled_scrolling) {
|
||||||
|
uint8_t display_scroll_left[] = {I2C_CMD, SCROLL_LEFT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL};
|
||||||
|
if (!oled_send_cmd(display_scroll_left, ARRAY_SIZE(display_scroll_left))) {
|
||||||
|
print("oled_scroll_left cmd failed\n");
|
||||||
|
return oled_scrolling;
|
||||||
|
}
|
||||||
|
oled_scrolling = true;
|
||||||
|
}
|
||||||
|
return oled_scrolling;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool oled_scroll_off(void) {
|
||||||
|
if (!oled_initialized) {
|
||||||
|
return !oled_scrolling;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oled_scrolling) {
|
||||||
|
static const uint8_t PROGMEM display_scroll_off[] = {I2C_CMD, DEACTIVATE_SCROLL};
|
||||||
|
if (!oled_send_cmd_P(display_scroll_off, ARRAY_SIZE(display_scroll_off))) {
|
||||||
|
print("oled_scroll_off cmd failed\n");
|
||||||
|
return oled_scrolling;
|
||||||
|
}
|
||||||
|
oled_scrolling = false;
|
||||||
|
oled_dirty = OLED_ALL_BLOCKS_MASK;
|
||||||
|
}
|
||||||
|
return !oled_scrolling;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_oled_scrolling(void) {
|
||||||
|
return oled_scrolling;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool oled_invert(bool invert) {
|
||||||
|
if (!oled_initialized) {
|
||||||
|
return oled_inverted;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invert && !oled_inverted) {
|
||||||
|
static const uint8_t PROGMEM display_inverted[] = {I2C_CMD, INVERT_DISPLAY};
|
||||||
|
if (!oled_send_cmd_P(display_inverted, ARRAY_SIZE(display_inverted))) {
|
||||||
|
print("oled_invert cmd failed\n");
|
||||||
|
return oled_inverted;
|
||||||
|
}
|
||||||
|
oled_inverted = true;
|
||||||
|
} else if (!invert && oled_inverted) {
|
||||||
|
static const uint8_t PROGMEM display_normal[] = {I2C_CMD, NORMAL_DISPLAY};
|
||||||
|
if (!oled_send_cmd_P(display_normal, ARRAY_SIZE(display_normal))) {
|
||||||
|
print("oled_invert cmd failed\n");
|
||||||
|
return oled_inverted;
|
||||||
|
}
|
||||||
|
oled_inverted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return oled_inverted;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t oled_max_chars(void) {
|
||||||
|
if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
|
||||||
|
return OLED_DISPLAY_WIDTH / OLED_FONT_WIDTH;
|
||||||
|
}
|
||||||
|
return OLED_DISPLAY_HEIGHT / OLED_FONT_WIDTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t oled_max_lines(void) {
|
||||||
|
if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
|
||||||
|
return OLED_DISPLAY_HEIGHT / OLED_FONT_HEIGHT;
|
||||||
|
}
|
||||||
|
return OLED_DISPLAY_WIDTH / OLED_FONT_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void oled_task(void) {
|
||||||
|
if (!oled_initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if OLED_UPDATE_INTERVAL > 0
|
||||||
|
if (timer_elapsed(oled_update_timeout) >= OLED_UPDATE_INTERVAL) {
|
||||||
|
oled_update_timeout = timer_read();
|
||||||
|
oled_set_cursor(0, 0);
|
||||||
|
oled_task_kb();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
oled_set_cursor(0, 0);
|
||||||
|
oled_task_kb();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if OLED_SCROLL_TIMEOUT > 0
|
||||||
|
if (oled_dirty && oled_scrolling) {
|
||||||
|
oled_scroll_timeout = timer_read32() + OLED_SCROLL_TIMEOUT;
|
||||||
|
oled_scroll_off();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Smart render system, no need to check for dirty
|
||||||
|
oled_render();
|
||||||
|
|
||||||
|
// Display timeout check
|
||||||
|
#if OLED_TIMEOUT > 0
|
||||||
|
if (oled_active && timer_expired32(timer_read32(), oled_timeout)) {
|
||||||
|
oled_off();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if OLED_SCROLL_TIMEOUT > 0
|
||||||
|
if (!oled_scrolling && timer_expired32(timer_read32(), oled_scroll_timeout)) {
|
||||||
|
# ifdef OLED_SCROLL_TIMEOUT_RIGHT
|
||||||
|
oled_scroll_right();
|
||||||
|
# else
|
||||||
|
oled_scroll_left();
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((weak)) bool oled_task_kb(void) {
|
||||||
|
return oled_task_user();
|
||||||
|
}
|
||||||
|
__attribute__((weak)) bool oled_task_user(void) {
|
||||||
|
return true;
|
||||||
|
}
|
@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
// an enumeration of the chips this driver supports
|
// an enumeration of the chips this driver supports
|
||||||
#define OLED_IC_SSD1306 0
|
#define OLED_IC_SSD1306 0
|
||||||
#define OLED_IC_SH1106 1
|
#define OLED_IC_SH1106 1
|
||||||
|
#define OLED_IC_SH1107 2
|
||||||
|
|
||||||
#if defined(OLED_DISPLAY_CUSTOM)
|
#if defined(OLED_DISPLAY_CUSTOM)
|
||||||
// Expected user to implement the necessary defines
|
// Expected user to implement the necessary defines
|
||||||
@ -68,6 +69,152 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
// If OLED_BLOCK_TYPE is uint8_t, these tables would look like:
|
// If OLED_BLOCK_TYPE is uint8_t, these tables would look like:
|
||||||
// #define OLED_SOURCE_MAP { 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120 }
|
// #define OLED_SOURCE_MAP { 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120 }
|
||||||
// #define OLED_TARGET_MAP { 56, 120, 48, 112, 40, 104, 32, 96, 24, 88, 16, 80, 8, 72, 0, 64 }
|
// #define OLED_TARGET_MAP { 56, 120, 48, 112, 40, 104, 32, 96, 24, 88, 16, 80, 8, 72, 0, 64 }
|
||||||
|
|
||||||
|
#elif defined(OLED_DISPLAY_64X32)
|
||||||
|
# ifndef OLED_DISPLAY_WIDTH
|
||||||
|
# define OLED_DISPLAY_WIDTH 64
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_DISPLAY_HEIGHT
|
||||||
|
# define OLED_DISPLAY_HEIGHT 32
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_COLUMN_OFFSET
|
||||||
|
# define OLED_COLUMN_OFFSET 32
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_MATRIX_SIZE
|
||||||
|
# define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH)
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_BLOCK_TYPE
|
||||||
|
# define OLED_BLOCK_TYPE uint8_t
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_BLOCK_COUNT
|
||||||
|
# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) // 8 (compile time mathed)
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_BLOCK_SIZE
|
||||||
|
# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) // 32 (compile time mathed)
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_COM_PINS
|
||||||
|
# define OLED_COM_PINS COM_PINS_ALT
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef OLED_SOURCE_MAP
|
||||||
|
# define OLED_SOURCE_MAP \
|
||||||
|
{ 0, 8, 16, 24 }
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_TARGET_MAP
|
||||||
|
# define OLED_TARGET_MAP \
|
||||||
|
{ 24, 16, 8, 0 }
|
||||||
|
# endif
|
||||||
|
|
||||||
|
#elif defined(OLED_DISPLAY_64X48)
|
||||||
|
# ifndef OLED_DISPLAY_WIDTH
|
||||||
|
# define OLED_DISPLAY_WIDTH 64
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_DISPLAY_HEIGHT
|
||||||
|
# define OLED_DISPLAY_HEIGHT 48
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_COLUMN_OFFSET
|
||||||
|
# define OLED_COLUMN_OFFSET 32
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_MATRIX_SIZE
|
||||||
|
# define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH)
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_BLOCK_TYPE
|
||||||
|
# define OLED_BLOCK_TYPE uint32_t
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_BLOCK_COUNT
|
||||||
|
# define OLED_BLOCK_COUNT 24
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_BLOCK_SIZE
|
||||||
|
# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_COM_PINS
|
||||||
|
# define OLED_COM_PINS COM_PINS_ALT
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef OLED_SOURCE_MAP
|
||||||
|
# define OLED_SOURCE_MAP \
|
||||||
|
{ 0, 8 }
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_TARGET_MAP
|
||||||
|
# define OLED_TARGET_MAP \
|
||||||
|
{ 8, 0 }
|
||||||
|
# endif
|
||||||
|
|
||||||
|
#elif defined(OLED_DISPLAY_64X128)
|
||||||
|
# ifndef OLED_DISPLAY_WIDTH
|
||||||
|
# define OLED_DISPLAY_WIDTH 64
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_DISPLAY_HEIGHT
|
||||||
|
# define OLED_DISPLAY_HEIGHT 128
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_IC
|
||||||
|
# define OLED_IC OLED_IC_SH1107
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_COM_PIN_OFFSET
|
||||||
|
# define OLED_COM_PIN_OFFSET 32
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_MATRIX_SIZE
|
||||||
|
# define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH)
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_BLOCK_TYPE
|
||||||
|
# define OLED_BLOCK_TYPE uint16_t
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_BLOCK_COUNT
|
||||||
|
# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8)
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_BLOCK_SIZE
|
||||||
|
# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_COM_PINS
|
||||||
|
# define OLED_COM_PINS COM_PINS_ALT
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef OLED_SOURCE_MAP
|
||||||
|
# define OLED_SOURCE_MAP \
|
||||||
|
{ 0, 8, 16, 24, 32, 40, 48, 56 }
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_TARGET_MAP
|
||||||
|
# define OLED_TARGET_MAP \
|
||||||
|
{ 56, 48, 40, 32, 24, 16, 8, 0 }
|
||||||
|
# endif
|
||||||
|
|
||||||
|
#elif defined(OLED_DISPLAY_128X128)
|
||||||
|
// Quad height 128x128
|
||||||
|
# ifndef OLED_DISPLAY_WIDTH
|
||||||
|
# define OLED_DISPLAY_WIDTH 128
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_DISPLAY_HEIGHT
|
||||||
|
# define OLED_DISPLAY_HEIGHT 128
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_IC
|
||||||
|
# define OLED_IC OLED_IC_SH1107
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_MATRIX_SIZE
|
||||||
|
# define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH) // 2048 (compile time mathed)
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_BLOCK_TYPE
|
||||||
|
# define OLED_BLOCK_TYPE uint32_t
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_BLOCK_COUNT
|
||||||
|
# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) // 32 (compile time mathed)
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_BLOCK_SIZE
|
||||||
|
# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) // 64 (compile time mathed)
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_COM_PINS
|
||||||
|
# define OLED_COM_PINS COM_PINS_ALT
|
||||||
|
# endif
|
||||||
|
|
||||||
|
// For 90 degree rotation, we map our internal matrix to oled matrix using fixed arrays
|
||||||
|
// The OLED writes to it's memory horizontally, starting top left, but our memory starts bottom left in this mode
|
||||||
|
# ifndef OLED_SOURCE_MAP
|
||||||
|
# define OLED_SOURCE_MAP \
|
||||||
|
{ 0, 8, 16, 24, 32, 40, 48, 56 }
|
||||||
|
# endif
|
||||||
|
# ifndef OLED_TARGET_MAP
|
||||||
|
# define OLED_TARGET_MAP \
|
||||||
|
{ 56, 48, 40, 32, 24, 16, 8, 0 }
|
||||||
|
# endif
|
||||||
#else // defined(OLED_DISPLAY_128X64)
|
#else // defined(OLED_DISPLAY_128X64)
|
||||||
// Default 128x32
|
// Default 128x32
|
||||||
# ifndef OLED_DISPLAY_WIDTH
|
# ifndef OLED_DISPLAY_WIDTH
|
||||||
@ -191,6 +338,12 @@ typedef enum {
|
|||||||
// Returns true if the OLED was initialized successfully
|
// Returns true if the OLED was initialized successfully
|
||||||
bool oled_init(oled_rotation_t rotation);
|
bool oled_init(oled_rotation_t rotation);
|
||||||
|
|
||||||
|
// Send commands and data to screen
|
||||||
|
bool oled_send_cmd(const uint8_t *data, uint16_t size);
|
||||||
|
bool oled_send_cmd_P(const uint8_t *data, uint16_t size);
|
||||||
|
bool oled_send_data(const uint8_t *data, uint16_t size);
|
||||||
|
void oled_driver_init(void);
|
||||||
|
|
||||||
// Called at the start of oled_init, weak function overridable by the user
|
// Called at the start of oled_init, weak function overridable by the user
|
||||||
// rotation - the value passed into oled_init
|
// rotation - the value passed into oled_init
|
||||||
// Return new oled_rotation_t if you want to override default rotation
|
// Return new oled_rotation_t if you want to override default rotation
|
||||||
|
@ -1,795 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2019 Ryan Caltabiano <https://github.com/XScorpion2>
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#include "i2c_master.h"
|
|
||||||
#include "oled_driver.h"
|
|
||||||
#include OLED_FONT_H
|
|
||||||
#include "timer.h"
|
|
||||||
#include "print.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "progmem.h"
|
|
||||||
|
|
||||||
#include "keyboard.h"
|
|
||||||
|
|
||||||
// Used commands from spec sheet: https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
|
|
||||||
// for SH1106: https://www.velleman.eu/downloads/29/infosheets/sh1106_datasheet.pdf
|
|
||||||
|
|
||||||
// Fundamental Commands
|
|
||||||
#define CONTRAST 0x81
|
|
||||||
#define DISPLAY_ALL_ON 0xA5
|
|
||||||
#define DISPLAY_ALL_ON_RESUME 0xA4
|
|
||||||
#define NORMAL_DISPLAY 0xA6
|
|
||||||
#define INVERT_DISPLAY 0xA7
|
|
||||||
#define DISPLAY_ON 0xAF
|
|
||||||
#define DISPLAY_OFF 0xAE
|
|
||||||
#define NOP 0xE3
|
|
||||||
|
|
||||||
// Scrolling Commands
|
|
||||||
#define ACTIVATE_SCROLL 0x2F
|
|
||||||
#define DEACTIVATE_SCROLL 0x2E
|
|
||||||
#define SCROLL_RIGHT 0x26
|
|
||||||
#define SCROLL_LEFT 0x27
|
|
||||||
#define SCROLL_RIGHT_UP 0x29
|
|
||||||
#define SCROLL_LEFT_UP 0x2A
|
|
||||||
|
|
||||||
// Addressing Setting Commands
|
|
||||||
#define MEMORY_MODE 0x20
|
|
||||||
#define COLUMN_ADDR 0x21
|
|
||||||
#define PAGE_ADDR 0x22
|
|
||||||
#define PAM_SETCOLUMN_LSB 0x00
|
|
||||||
#define PAM_SETCOLUMN_MSB 0x10
|
|
||||||
#define PAM_PAGE_ADDR 0xB0 // 0xb0 -- 0xb7
|
|
||||||
|
|
||||||
// Hardware Configuration Commands
|
|
||||||
#define DISPLAY_START_LINE 0x40
|
|
||||||
#define SEGMENT_REMAP 0xA0
|
|
||||||
#define SEGMENT_REMAP_INV 0xA1
|
|
||||||
#define MULTIPLEX_RATIO 0xA8
|
|
||||||
#define COM_SCAN_INC 0xC0
|
|
||||||
#define COM_SCAN_DEC 0xC8
|
|
||||||
#define DISPLAY_OFFSET 0xD3
|
|
||||||
#define COM_PINS 0xDA
|
|
||||||
#define COM_PINS_SEQ 0x02
|
|
||||||
#define COM_PINS_ALT 0x12
|
|
||||||
#define COM_PINS_SEQ_LR 0x22
|
|
||||||
#define COM_PINS_ALT_LR 0x32
|
|
||||||
|
|
||||||
// Timing & Driving Commands
|
|
||||||
#define DISPLAY_CLOCK 0xD5
|
|
||||||
#define PRE_CHARGE_PERIOD 0xD9
|
|
||||||
#define VCOM_DETECT 0xDB
|
|
||||||
|
|
||||||
// Advance Graphic Commands
|
|
||||||
#define FADE_BLINK 0x23
|
|
||||||
#define ENABLE_FADE 0x20
|
|
||||||
#define ENABLE_BLINK 0x30
|
|
||||||
|
|
||||||
// Charge Pump Commands
|
|
||||||
#define CHARGE_PUMP 0x8D
|
|
||||||
|
|
||||||
// Misc defines
|
|
||||||
#ifndef OLED_BLOCK_COUNT
|
|
||||||
# define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8)
|
|
||||||
#endif
|
|
||||||
#ifndef OLED_BLOCK_SIZE
|
|
||||||
# define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define OLED_ALL_BLOCKS_MASK (((((OLED_BLOCK_TYPE)1 << (OLED_BLOCK_COUNT - 1)) - 1) << 1) | 1)
|
|
||||||
|
|
||||||
// i2c defines
|
|
||||||
#define I2C_CMD 0x00
|
|
||||||
#define I2C_DATA 0x40
|
|
||||||
#if defined(__AVR__)
|
|
||||||
# define I2C_TRANSMIT_P(data) i2c_transmit_P((OLED_DISPLAY_ADDRESS << 1), &data[0], sizeof(data), OLED_I2C_TIMEOUT)
|
|
||||||
#else // defined(__AVR__)
|
|
||||||
# define I2C_TRANSMIT_P(data) i2c_transmit((OLED_DISPLAY_ADDRESS << 1), &data[0], sizeof(data), OLED_I2C_TIMEOUT)
|
|
||||||
#endif // defined(__AVR__)
|
|
||||||
#define I2C_TRANSMIT(data) i2c_transmit((OLED_DISPLAY_ADDRESS << 1), &data[0], sizeof(data), OLED_I2C_TIMEOUT)
|
|
||||||
#define I2C_WRITE_REG(mode, data, size) i2c_writeReg((OLED_DISPLAY_ADDRESS << 1), mode, data, size, OLED_I2C_TIMEOUT)
|
|
||||||
|
|
||||||
#define HAS_FLAGS(bits, flags) ((bits & flags) == flags)
|
|
||||||
|
|
||||||
// Display buffer's is the same as the OLED memory layout
|
|
||||||
// this is so we don't end up with rounding errors with
|
|
||||||
// parts of the display unusable or don't get cleared correctly
|
|
||||||
// and also allows for drawing & inverting
|
|
||||||
uint8_t oled_buffer[OLED_MATRIX_SIZE];
|
|
||||||
uint8_t * oled_cursor;
|
|
||||||
OLED_BLOCK_TYPE oled_dirty = 0;
|
|
||||||
bool oled_initialized = false;
|
|
||||||
bool oled_active = false;
|
|
||||||
bool oled_scrolling = false;
|
|
||||||
bool oled_inverted = false;
|
|
||||||
uint8_t oled_brightness = OLED_BRIGHTNESS;
|
|
||||||
oled_rotation_t oled_rotation = 0;
|
|
||||||
uint8_t oled_rotation_width = 0;
|
|
||||||
uint8_t oled_scroll_speed = 0; // this holds the speed after being remapped to ssd1306 internal values
|
|
||||||
uint8_t oled_scroll_start = 0;
|
|
||||||
uint8_t oled_scroll_end = 7;
|
|
||||||
#if OLED_TIMEOUT > 0
|
|
||||||
uint32_t oled_timeout;
|
|
||||||
#endif
|
|
||||||
#if OLED_SCROLL_TIMEOUT > 0
|
|
||||||
uint32_t oled_scroll_timeout;
|
|
||||||
#endif
|
|
||||||
#if OLED_UPDATE_INTERVAL > 0
|
|
||||||
uint16_t oled_update_timeout;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Internal variables to reduce math instructions
|
|
||||||
|
|
||||||
#if defined(__AVR__)
|
|
||||||
// identical to i2c_transmit, but for PROGMEM since all initialization is in PROGMEM arrays currently
|
|
||||||
// probably should move this into i2c_master...
|
|
||||||
static i2c_status_t i2c_transmit_P(uint8_t address, const uint8_t *data, uint16_t length, uint16_t timeout) {
|
|
||||||
i2c_status_t status = i2c_start(address | I2C_WRITE, timeout);
|
|
||||||
|
|
||||||
for (uint16_t i = 0; i < length && status >= 0; i++) {
|
|
||||||
status = i2c_write(pgm_read_byte((const char *)data++), timeout);
|
|
||||||
if (status) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
i2c_stop();
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Flips the rendering bits for a character at the current cursor position
|
|
||||||
static void InvertCharacter(uint8_t *cursor) {
|
|
||||||
const uint8_t *end = cursor + OLED_FONT_WIDTH;
|
|
||||||
while (cursor < end) {
|
|
||||||
*cursor = ~(*cursor);
|
|
||||||
cursor++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool oled_init(oled_rotation_t rotation) {
|
|
||||||
#if defined(USE_I2C) && defined(SPLIT_KEYBOARD)
|
|
||||||
if (!is_keyboard_master()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
oled_rotation = oled_init_user(oled_init_kb(rotation));
|
|
||||||
if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
|
|
||||||
oled_rotation_width = OLED_DISPLAY_WIDTH;
|
|
||||||
} else {
|
|
||||||
oled_rotation_width = OLED_DISPLAY_HEIGHT;
|
|
||||||
}
|
|
||||||
i2c_init();
|
|
||||||
|
|
||||||
static const uint8_t PROGMEM display_setup1[] = {
|
|
||||||
I2C_CMD,
|
|
||||||
DISPLAY_OFF,
|
|
||||||
DISPLAY_CLOCK,
|
|
||||||
0x80,
|
|
||||||
MULTIPLEX_RATIO,
|
|
||||||
OLED_DISPLAY_HEIGHT - 1,
|
|
||||||
DISPLAY_OFFSET,
|
|
||||||
0x00,
|
|
||||||
DISPLAY_START_LINE | 0x00,
|
|
||||||
CHARGE_PUMP,
|
|
||||||
0x14,
|
|
||||||
#if (OLED_IC != OLED_IC_SH1106)
|
|
||||||
// MEMORY_MODE is unsupported on SH1106 (Page Addressing only)
|
|
||||||
MEMORY_MODE,
|
|
||||||
0x00, // Horizontal addressing mode
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
if (I2C_TRANSMIT_P(display_setup1) != I2C_STATUS_SUCCESS) {
|
|
||||||
print("oled_init cmd set 1 failed\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_180)) {
|
|
||||||
static const uint8_t PROGMEM display_normal[] = {I2C_CMD, SEGMENT_REMAP_INV, COM_SCAN_DEC};
|
|
||||||
if (I2C_TRANSMIT_P(display_normal) != I2C_STATUS_SUCCESS) {
|
|
||||||
print("oled_init cmd normal rotation failed\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
static const uint8_t PROGMEM display_flipped[] = {I2C_CMD, SEGMENT_REMAP, COM_SCAN_INC};
|
|
||||||
if (I2C_TRANSMIT_P(display_flipped) != I2C_STATUS_SUCCESS) {
|
|
||||||
print("display_flipped failed\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const uint8_t PROGMEM display_setup2[] = {I2C_CMD, COM_PINS, OLED_COM_PINS, CONTRAST, OLED_BRIGHTNESS, PRE_CHARGE_PERIOD, 0xF1, VCOM_DETECT, 0x20, DISPLAY_ALL_ON_RESUME, NORMAL_DISPLAY, DEACTIVATE_SCROLL, DISPLAY_ON};
|
|
||||||
if (I2C_TRANSMIT_P(display_setup2) != I2C_STATUS_SUCCESS) {
|
|
||||||
print("display_setup2 failed\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if OLED_TIMEOUT > 0
|
|
||||||
oled_timeout = timer_read32() + OLED_TIMEOUT;
|
|
||||||
#endif
|
|
||||||
#if OLED_SCROLL_TIMEOUT > 0
|
|
||||||
oled_scroll_timeout = timer_read32() + OLED_SCROLL_TIMEOUT;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
oled_clear();
|
|
||||||
oled_initialized = true;
|
|
||||||
oled_active = true;
|
|
||||||
oled_scrolling = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((weak)) oled_rotation_t oled_init_kb(oled_rotation_t rotation) {
|
|
||||||
return rotation;
|
|
||||||
}
|
|
||||||
__attribute__((weak)) oled_rotation_t oled_init_user(oled_rotation_t rotation) {
|
|
||||||
return rotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
void oled_clear(void) {
|
|
||||||
memset(oled_buffer, 0, sizeof(oled_buffer));
|
|
||||||
oled_cursor = &oled_buffer[0];
|
|
||||||
oled_dirty = OLED_ALL_BLOCKS_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void calc_bounds(uint8_t update_start, uint8_t *cmd_array) {
|
|
||||||
// Calculate commands to set memory addressing bounds.
|
|
||||||
uint8_t start_page = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_WIDTH;
|
|
||||||
uint8_t start_column = OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_WIDTH;
|
|
||||||
#if (OLED_IC == OLED_IC_SH1106)
|
|
||||||
// Commands for Page Addressing Mode. Sets starting page and column; has no end bound.
|
|
||||||
// Column value must be split into high and low nybble and sent as two commands.
|
|
||||||
cmd_array[0] = PAM_PAGE_ADDR | start_page;
|
|
||||||
cmd_array[1] = PAM_SETCOLUMN_LSB | ((OLED_COLUMN_OFFSET + start_column) & 0x0f);
|
|
||||||
cmd_array[2] = PAM_SETCOLUMN_MSB | ((OLED_COLUMN_OFFSET + start_column) >> 4 & 0x0f);
|
|
||||||
cmd_array[3] = NOP;
|
|
||||||
cmd_array[4] = NOP;
|
|
||||||
cmd_array[5] = NOP;
|
|
||||||
#else
|
|
||||||
// Commands for use in Horizontal Addressing mode.
|
|
||||||
cmd_array[1] = start_column;
|
|
||||||
cmd_array[4] = start_page;
|
|
||||||
cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) % OLED_DISPLAY_WIDTH + cmd_array[1];
|
|
||||||
cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) / OLED_DISPLAY_WIDTH - 1;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void calc_bounds_90(uint8_t update_start, uint8_t *cmd_array) {
|
|
||||||
cmd_array[1] = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_HEIGHT * 8;
|
|
||||||
cmd_array[4] = OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_HEIGHT;
|
|
||||||
cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) / OLED_DISPLAY_HEIGHT * 8 - 1 + cmd_array[1];
|
|
||||||
;
|
|
||||||
cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) % OLED_DISPLAY_HEIGHT / 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t crot(uint8_t a, int8_t n) {
|
|
||||||
const uint8_t mask = 0x7;
|
|
||||||
n &= mask;
|
|
||||||
return a << n | a >> (-n & mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rotate_90(const uint8_t *src, uint8_t *dest) {
|
|
||||||
for (uint8_t i = 0, shift = 7; i < 8; ++i, --shift) {
|
|
||||||
uint8_t selector = (1 << i);
|
|
||||||
for (uint8_t j = 0; j < 8; ++j) {
|
|
||||||
dest[i] |= crot(src[j] & selector, shift - (int8_t)j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void oled_render(void) {
|
|
||||||
// Do we have work to do?
|
|
||||||
oled_dirty &= OLED_ALL_BLOCKS_MASK;
|
|
||||||
if (!oled_dirty || !oled_initialized || oled_scrolling) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turn on display if it is off
|
|
||||||
oled_on();
|
|
||||||
|
|
||||||
uint8_t update_start = 0;
|
|
||||||
uint8_t num_processed = 0;
|
|
||||||
while (oled_dirty && num_processed++ < OLED_UPDATE_PROCESS_LIMIT) { // render all dirty blocks (up to the configured limit)
|
|
||||||
// Find next dirty block
|
|
||||||
while (!(oled_dirty & ((OLED_BLOCK_TYPE)1 << update_start))) {
|
|
||||||
++update_start;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set column & page position
|
|
||||||
static uint8_t display_start[] = {I2C_CMD, COLUMN_ADDR, 0, OLED_DISPLAY_WIDTH - 1, PAGE_ADDR, 0, OLED_DISPLAY_HEIGHT / 8 - 1};
|
|
||||||
if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
|
|
||||||
calc_bounds(update_start, &display_start[1]); // Offset from I2C_CMD byte at the start
|
|
||||||
} else {
|
|
||||||
calc_bounds_90(update_start, &display_start[1]); // Offset from I2C_CMD byte at the start
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send column & page position
|
|
||||||
if (I2C_TRANSMIT(display_start) != I2C_STATUS_SUCCESS) {
|
|
||||||
print("oled_render offset command failed\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
|
|
||||||
// Send render data chunk as is
|
|
||||||
if (I2C_WRITE_REG(I2C_DATA, &oled_buffer[OLED_BLOCK_SIZE * update_start], OLED_BLOCK_SIZE) != I2C_STATUS_SUCCESS) {
|
|
||||||
print("oled_render data failed\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Rotate the render chunks
|
|
||||||
const static uint8_t source_map[] = OLED_SOURCE_MAP;
|
|
||||||
const static uint8_t target_map[] = OLED_TARGET_MAP;
|
|
||||||
|
|
||||||
static uint8_t temp_buffer[OLED_BLOCK_SIZE];
|
|
||||||
memset(temp_buffer, 0, sizeof(temp_buffer));
|
|
||||||
for (uint8_t i = 0; i < sizeof(source_map); ++i) {
|
|
||||||
rotate_90(&oled_buffer[OLED_BLOCK_SIZE * update_start + source_map[i]], &temp_buffer[target_map[i]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send render data chunk after rotating
|
|
||||||
if (I2C_WRITE_REG(I2C_DATA, &temp_buffer[0], OLED_BLOCK_SIZE) != I2C_STATUS_SUCCESS) {
|
|
||||||
print("oled_render90 data failed\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear dirty flag of just rendered block
|
|
||||||
oled_dirty &= ~((OLED_BLOCK_TYPE)1 << update_start);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void oled_set_cursor(uint8_t col, uint8_t line) {
|
|
||||||
uint16_t index = line * oled_rotation_width + col * OLED_FONT_WIDTH;
|
|
||||||
|
|
||||||
// Out of bounds?
|
|
||||||
if (index >= OLED_MATRIX_SIZE) {
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
oled_cursor = &oled_buffer[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
void oled_advance_page(bool clearPageRemainder) {
|
|
||||||
uint16_t index = oled_cursor - &oled_buffer[0];
|
|
||||||
uint8_t remaining = oled_rotation_width - (index % oled_rotation_width);
|
|
||||||
|
|
||||||
if (clearPageRemainder) {
|
|
||||||
// Remaining Char count
|
|
||||||
remaining = remaining / OLED_FONT_WIDTH;
|
|
||||||
|
|
||||||
// Write empty character until next line
|
|
||||||
while (remaining--)
|
|
||||||
oled_write_char(' ', false);
|
|
||||||
} else {
|
|
||||||
// Next page index out of bounds?
|
|
||||||
if (index + remaining >= OLED_MATRIX_SIZE) {
|
|
||||||
index = 0;
|
|
||||||
remaining = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
oled_cursor = &oled_buffer[index + remaining];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void oled_advance_char(void) {
|
|
||||||
uint16_t nextIndex = oled_cursor - &oled_buffer[0] + OLED_FONT_WIDTH;
|
|
||||||
uint8_t remainingSpace = oled_rotation_width - (nextIndex % oled_rotation_width);
|
|
||||||
|
|
||||||
// Do we have enough space on the current line for the next character
|
|
||||||
if (remainingSpace < OLED_FONT_WIDTH) {
|
|
||||||
nextIndex += remainingSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Did we go out of bounds
|
|
||||||
if (nextIndex >= OLED_MATRIX_SIZE) {
|
|
||||||
nextIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update cursor position
|
|
||||||
oled_cursor = &oled_buffer[nextIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Main handler that writes character data to the display buffer
|
|
||||||
void oled_write_char(const char data, bool invert) {
|
|
||||||
// Advance to the next line if newline
|
|
||||||
if (data == '\n') {
|
|
||||||
// Old source wrote ' ' until end of line...
|
|
||||||
oled_advance_page(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data == '\r') {
|
|
||||||
oled_advance_page(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy the current render buffer to check for dirty after
|
|
||||||
static uint8_t oled_temp_buffer[OLED_FONT_WIDTH];
|
|
||||||
memcpy(&oled_temp_buffer, oled_cursor, OLED_FONT_WIDTH);
|
|
||||||
|
|
||||||
_Static_assert(sizeof(font) >= ((OLED_FONT_END + 1 - OLED_FONT_START) * OLED_FONT_WIDTH), "OLED_FONT_END references outside array");
|
|
||||||
|
|
||||||
// set the reder buffer data
|
|
||||||
uint8_t cast_data = (uint8_t)data; // font based on unsigned type for index
|
|
||||||
if (cast_data < OLED_FONT_START || cast_data > OLED_FONT_END) {
|
|
||||||
memset(oled_cursor, 0x00, OLED_FONT_WIDTH);
|
|
||||||
} else {
|
|
||||||
const uint8_t *glyph = &font[(cast_data - OLED_FONT_START) * OLED_FONT_WIDTH];
|
|
||||||
memcpy_P(oled_cursor, glyph, OLED_FONT_WIDTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invert if needed
|
|
||||||
if (invert) {
|
|
||||||
InvertCharacter(oled_cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dirty check
|
|
||||||
if (memcmp(&oled_temp_buffer, oled_cursor, OLED_FONT_WIDTH)) {
|
|
||||||
uint16_t index = oled_cursor - &oled_buffer[0];
|
|
||||||
oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE));
|
|
||||||
// Edgecase check if the written data spans the 2 chunks
|
|
||||||
oled_dirty |= ((OLED_BLOCK_TYPE)1 << ((index + OLED_FONT_WIDTH - 1) / OLED_BLOCK_SIZE));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally move to the next char
|
|
||||||
oled_advance_char();
|
|
||||||
}
|
|
||||||
|
|
||||||
void oled_write(const char *data, bool invert) {
|
|
||||||
const char *end = data + strlen(data);
|
|
||||||
while (data < end) {
|
|
||||||
oled_write_char(*data, invert);
|
|
||||||
data++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void oled_write_ln(const char *data, bool invert) {
|
|
||||||
oled_write(data, invert);
|
|
||||||
oled_advance_page(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void oled_pan(bool left) {
|
|
||||||
uint16_t i = 0;
|
|
||||||
for (uint16_t y = 0; y < OLED_DISPLAY_HEIGHT / 8; y++) {
|
|
||||||
if (left) {
|
|
||||||
for (uint16_t x = 0; x < OLED_DISPLAY_WIDTH - 1; x++) {
|
|
||||||
i = y * OLED_DISPLAY_WIDTH + x;
|
|
||||||
oled_buffer[i] = oled_buffer[i + 1];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (uint16_t x = OLED_DISPLAY_WIDTH - 1; x > 0; x--) {
|
|
||||||
i = y * OLED_DISPLAY_WIDTH + x;
|
|
||||||
oled_buffer[i] = oled_buffer[i - 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
oled_dirty = OLED_ALL_BLOCKS_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
oled_buffer_reader_t oled_read_raw(uint16_t start_index) {
|
|
||||||
if (start_index > OLED_MATRIX_SIZE) start_index = OLED_MATRIX_SIZE;
|
|
||||||
oled_buffer_reader_t ret_reader;
|
|
||||||
ret_reader.current_element = &oled_buffer[start_index];
|
|
||||||
ret_reader.remaining_element_count = OLED_MATRIX_SIZE - start_index;
|
|
||||||
return ret_reader;
|
|
||||||
}
|
|
||||||
|
|
||||||
void oled_write_raw_byte(const char data, uint16_t index) {
|
|
||||||
if (index > OLED_MATRIX_SIZE) index = OLED_MATRIX_SIZE;
|
|
||||||
if (oled_buffer[index] == data) return;
|
|
||||||
oled_buffer[index] = data;
|
|
||||||
oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE));
|
|
||||||
}
|
|
||||||
|
|
||||||
void oled_write_raw(const char *data, uint16_t size) {
|
|
||||||
uint16_t cursor_start_index = oled_cursor - &oled_buffer[0];
|
|
||||||
if ((size + cursor_start_index) > OLED_MATRIX_SIZE) size = OLED_MATRIX_SIZE - cursor_start_index;
|
|
||||||
for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) {
|
|
||||||
uint8_t c = *data++;
|
|
||||||
if (oled_buffer[i] == c) continue;
|
|
||||||
oled_buffer[i] = c;
|
|
||||||
oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void oled_write_pixel(uint8_t x, uint8_t y, bool on) {
|
|
||||||
if (x >= oled_rotation_width) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint16_t index = x + (y / 8) * oled_rotation_width;
|
|
||||||
if (index >= OLED_MATRIX_SIZE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint8_t data = oled_buffer[index];
|
|
||||||
if (on) {
|
|
||||||
data |= (1 << (y % 8));
|
|
||||||
} else {
|
|
||||||
data &= ~(1 << (y % 8));
|
|
||||||
}
|
|
||||||
if (oled_buffer[index] != data) {
|
|
||||||
oled_buffer[index] = data;
|
|
||||||
oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__AVR__)
|
|
||||||
void oled_write_P(const char *data, bool invert) {
|
|
||||||
uint8_t c = pgm_read_byte(data);
|
|
||||||
while (c != 0) {
|
|
||||||
oled_write_char(c, invert);
|
|
||||||
c = pgm_read_byte(++data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void oled_write_ln_P(const char *data, bool invert) {
|
|
||||||
oled_write_P(data, invert);
|
|
||||||
oled_advance_page(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void oled_write_raw_P(const char *data, uint16_t size) {
|
|
||||||
uint16_t cursor_start_index = oled_cursor - &oled_buffer[0];
|
|
||||||
if ((size + cursor_start_index) > OLED_MATRIX_SIZE) size = OLED_MATRIX_SIZE - cursor_start_index;
|
|
||||||
for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) {
|
|
||||||
uint8_t c = pgm_read_byte(data++);
|
|
||||||
if (oled_buffer[i] == c) continue;
|
|
||||||
oled_buffer[i] = c;
|
|
||||||
oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // defined(__AVR__)
|
|
||||||
|
|
||||||
bool oled_on(void) {
|
|
||||||
if (!oled_initialized) {
|
|
||||||
return oled_active;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if OLED_TIMEOUT > 0
|
|
||||||
oled_timeout = timer_read32() + OLED_TIMEOUT;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const uint8_t PROGMEM display_on[] =
|
|
||||||
#ifdef OLED_FADE_OUT
|
|
||||||
{I2C_CMD, FADE_BLINK, 0x00};
|
|
||||||
#else
|
|
||||||
{I2C_CMD, DISPLAY_ON};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!oled_active) {
|
|
||||||
if (I2C_TRANSMIT_P(display_on) != I2C_STATUS_SUCCESS) {
|
|
||||||
print("oled_on cmd failed\n");
|
|
||||||
return oled_active;
|
|
||||||
}
|
|
||||||
oled_active = true;
|
|
||||||
}
|
|
||||||
return oled_active;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool oled_off(void) {
|
|
||||||
if (!oled_initialized) {
|
|
||||||
return !oled_active;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const uint8_t PROGMEM display_off[] =
|
|
||||||
#ifdef OLED_FADE_OUT
|
|
||||||
{I2C_CMD, FADE_BLINK, ENABLE_FADE | OLED_FADE_OUT_INTERVAL};
|
|
||||||
#else
|
|
||||||
{I2C_CMD, DISPLAY_OFF};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (oled_active) {
|
|
||||||
if (I2C_TRANSMIT_P(display_off) != I2C_STATUS_SUCCESS) {
|
|
||||||
print("oled_off cmd failed\n");
|
|
||||||
return oled_active;
|
|
||||||
}
|
|
||||||
oled_active = false;
|
|
||||||
}
|
|
||||||
return !oled_active;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_oled_on(void) {
|
|
||||||
return oled_active;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t oled_set_brightness(uint8_t level) {
|
|
||||||
if (!oled_initialized) {
|
|
||||||
return oled_brightness;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t set_contrast[] = {I2C_CMD, CONTRAST, level};
|
|
||||||
if (oled_brightness != level) {
|
|
||||||
if (I2C_TRANSMIT(set_contrast) != I2C_STATUS_SUCCESS) {
|
|
||||||
print("set_brightness cmd failed\n");
|
|
||||||
return oled_brightness;
|
|
||||||
}
|
|
||||||
oled_brightness = level;
|
|
||||||
}
|
|
||||||
return oled_brightness;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t oled_get_brightness(void) {
|
|
||||||
return oled_brightness;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the specific 8 lines rows of the screen to scroll.
|
|
||||||
// 0 is the default for start, and 7 for end, which is the entire
|
|
||||||
// height of the screen. For 128x32 screens, rows 4-7 are not used.
|
|
||||||
void oled_scroll_set_area(uint8_t start_line, uint8_t end_line) {
|
|
||||||
oled_scroll_start = start_line;
|
|
||||||
oled_scroll_end = end_line;
|
|
||||||
}
|
|
||||||
|
|
||||||
void oled_scroll_set_speed(uint8_t speed) {
|
|
||||||
// Sets the speed for scrolling... does not take effect
|
|
||||||
// until scrolling is either started or restarted
|
|
||||||
// the ssd1306 supports 8 speeds
|
|
||||||
// FrameRate2 speed = 7
|
|
||||||
// FrameRate3 speed = 4
|
|
||||||
// FrameRate4 speed = 5
|
|
||||||
// FrameRate5 speed = 0
|
|
||||||
// FrameRate25 speed = 6
|
|
||||||
// FrameRate64 speed = 1
|
|
||||||
// FrameRate128 speed = 2
|
|
||||||
// FrameRate256 speed = 3
|
|
||||||
// for ease of use these are remaped here to be in order
|
|
||||||
static const uint8_t scroll_remap[8] = {7, 4, 5, 0, 6, 1, 2, 3};
|
|
||||||
oled_scroll_speed = scroll_remap[speed];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool oled_scroll_right(void) {
|
|
||||||
if (!oled_initialized) {
|
|
||||||
return oled_scrolling;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dont enable scrolling if we need to update the display
|
|
||||||
// This prevents scrolling of bad data from starting the scroll too early after init
|
|
||||||
if (!oled_dirty && !oled_scrolling) {
|
|
||||||
uint8_t display_scroll_right[] = {I2C_CMD, SCROLL_RIGHT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL};
|
|
||||||
if (I2C_TRANSMIT(display_scroll_right) != I2C_STATUS_SUCCESS) {
|
|
||||||
print("oled_scroll_right cmd failed\n");
|
|
||||||
return oled_scrolling;
|
|
||||||
}
|
|
||||||
oled_scrolling = true;
|
|
||||||
}
|
|
||||||
return oled_scrolling;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool oled_scroll_left(void) {
|
|
||||||
if (!oled_initialized) {
|
|
||||||
return oled_scrolling;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dont enable scrolling if we need to update the display
|
|
||||||
// This prevents scrolling of bad data from starting the scroll too early after init
|
|
||||||
if (!oled_dirty && !oled_scrolling) {
|
|
||||||
uint8_t display_scroll_left[] = {I2C_CMD, SCROLL_LEFT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL};
|
|
||||||
if (I2C_TRANSMIT(display_scroll_left) != I2C_STATUS_SUCCESS) {
|
|
||||||
print("oled_scroll_left cmd failed\n");
|
|
||||||
return oled_scrolling;
|
|
||||||
}
|
|
||||||
oled_scrolling = true;
|
|
||||||
}
|
|
||||||
return oled_scrolling;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool oled_scroll_off(void) {
|
|
||||||
if (!oled_initialized) {
|
|
||||||
return !oled_scrolling;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oled_scrolling) {
|
|
||||||
static const uint8_t PROGMEM display_scroll_off[] = {I2C_CMD, DEACTIVATE_SCROLL};
|
|
||||||
if (I2C_TRANSMIT_P(display_scroll_off) != I2C_STATUS_SUCCESS) {
|
|
||||||
print("oled_scroll_off cmd failed\n");
|
|
||||||
return oled_scrolling;
|
|
||||||
}
|
|
||||||
oled_scrolling = false;
|
|
||||||
oled_dirty = OLED_ALL_BLOCKS_MASK;
|
|
||||||
}
|
|
||||||
return !oled_scrolling;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_oled_scrolling(void) {
|
|
||||||
return oled_scrolling;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool oled_invert(bool invert) {
|
|
||||||
if (!oled_initialized) {
|
|
||||||
return oled_inverted;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (invert && !oled_inverted) {
|
|
||||||
static const uint8_t PROGMEM display_inverted[] = {I2C_CMD, INVERT_DISPLAY};
|
|
||||||
if (I2C_TRANSMIT_P(display_inverted) != I2C_STATUS_SUCCESS) {
|
|
||||||
print("oled_invert cmd failed\n");
|
|
||||||
return oled_inverted;
|
|
||||||
}
|
|
||||||
oled_inverted = true;
|
|
||||||
} else if (!invert && oled_inverted) {
|
|
||||||
static const uint8_t PROGMEM display_normal[] = {I2C_CMD, NORMAL_DISPLAY};
|
|
||||||
if (I2C_TRANSMIT_P(display_normal) != I2C_STATUS_SUCCESS) {
|
|
||||||
print("oled_invert cmd failed\n");
|
|
||||||
return oled_inverted;
|
|
||||||
}
|
|
||||||
oled_inverted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return oled_inverted;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t oled_max_chars(void) {
|
|
||||||
if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
|
|
||||||
return OLED_DISPLAY_WIDTH / OLED_FONT_WIDTH;
|
|
||||||
}
|
|
||||||
return OLED_DISPLAY_HEIGHT / OLED_FONT_WIDTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t oled_max_lines(void) {
|
|
||||||
if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
|
|
||||||
return OLED_DISPLAY_HEIGHT / OLED_FONT_HEIGHT;
|
|
||||||
}
|
|
||||||
return OLED_DISPLAY_WIDTH / OLED_FONT_HEIGHT;
|
|
||||||
}
|
|
||||||
|
|
||||||
void oled_task(void) {
|
|
||||||
if (!oled_initialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if OLED_UPDATE_INTERVAL > 0
|
|
||||||
if (timer_elapsed(oled_update_timeout) >= OLED_UPDATE_INTERVAL) {
|
|
||||||
oled_update_timeout = timer_read();
|
|
||||||
oled_set_cursor(0, 0);
|
|
||||||
oled_task_kb();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
oled_set_cursor(0, 0);
|
|
||||||
oled_task_kb();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if OLED_SCROLL_TIMEOUT > 0
|
|
||||||
if (oled_dirty && oled_scrolling) {
|
|
||||||
oled_scroll_timeout = timer_read32() + OLED_SCROLL_TIMEOUT;
|
|
||||||
oled_scroll_off();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Smart render system, no need to check for dirty
|
|
||||||
oled_render();
|
|
||||||
|
|
||||||
// Display timeout check
|
|
||||||
#if OLED_TIMEOUT > 0
|
|
||||||
if (oled_active && timer_expired32(timer_read32(), oled_timeout)) {
|
|
||||||
oled_off();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if OLED_SCROLL_TIMEOUT > 0
|
|
||||||
if (!oled_scrolling && timer_expired32(timer_read32(), oled_scroll_timeout)) {
|
|
||||||
# ifdef OLED_SCROLL_TIMEOUT_RIGHT
|
|
||||||
oled_scroll_right();
|
|
||||||
# else
|
|
||||||
oled_scroll_left();
|
|
||||||
# endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((weak)) bool oled_task_kb(void) {
|
|
||||||
return oled_task_user();
|
|
||||||
}
|
|
||||||
__attribute__((weak)) bool oled_task_user(void) {
|
|
||||||
return true;
|
|
||||||
}
|
|
@ -10,8 +10,8 @@
|
|||||||
// Base SPI support
|
// Base SPI support
|
||||||
|
|
||||||
bool qp_comms_spi_init(painter_device_t device) {
|
bool qp_comms_spi_init(painter_device_t device) {
|
||||||
struct painter_driver_t * driver = (struct painter_driver_t *)device;
|
painter_driver_t * driver = (painter_driver_t *)device;
|
||||||
struct qp_comms_spi_config_t *comms_config = (struct qp_comms_spi_config_t *)driver->comms_config;
|
qp_comms_spi_config_t *comms_config = (qp_comms_spi_config_t *)driver->comms_config;
|
||||||
|
|
||||||
// Initialize the SPI peripheral
|
// Initialize the SPI peripheral
|
||||||
spi_init();
|
spi_init();
|
||||||
@ -24,8 +24,8 @@ bool qp_comms_spi_init(painter_device_t device) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool qp_comms_spi_start(painter_device_t device) {
|
bool qp_comms_spi_start(painter_device_t device) {
|
||||||
struct painter_driver_t * driver = (struct painter_driver_t *)device;
|
painter_driver_t * driver = (painter_driver_t *)device;
|
||||||
struct qp_comms_spi_config_t *comms_config = (struct qp_comms_spi_config_t *)driver->comms_config;
|
qp_comms_spi_config_t *comms_config = (qp_comms_spi_config_t *)driver->comms_config;
|
||||||
|
|
||||||
return spi_start(comms_config->chip_select_pin, comms_config->lsb_first, comms_config->mode, comms_config->divisor);
|
return spi_start(comms_config->chip_select_pin, comms_config->lsb_first, comms_config->mode, comms_config->divisor);
|
||||||
}
|
}
|
||||||
@ -33,8 +33,10 @@ bool qp_comms_spi_start(painter_device_t device) {
|
|||||||
uint32_t qp_comms_spi_send_data(painter_device_t device, const void *data, uint32_t byte_count) {
|
uint32_t qp_comms_spi_send_data(painter_device_t device, const void *data, uint32_t byte_count) {
|
||||||
uint32_t bytes_remaining = byte_count;
|
uint32_t bytes_remaining = byte_count;
|
||||||
const uint8_t *p = (const uint8_t *)data;
|
const uint8_t *p = (const uint8_t *)data;
|
||||||
|
const uint32_t max_msg_length = 1024;
|
||||||
|
|
||||||
while (bytes_remaining > 0) {
|
while (bytes_remaining > 0) {
|
||||||
uint32_t bytes_this_loop = bytes_remaining < 1024 ? bytes_remaining : 1024;
|
uint32_t bytes_this_loop = QP_MIN(bytes_remaining, max_msg_length);
|
||||||
spi_transmit(p, bytes_this_loop);
|
spi_transmit(p, bytes_this_loop);
|
||||||
p += bytes_this_loop;
|
p += bytes_this_loop;
|
||||||
bytes_remaining -= bytes_this_loop;
|
bytes_remaining -= bytes_this_loop;
|
||||||
@ -44,13 +46,13 @@ uint32_t qp_comms_spi_send_data(painter_device_t device, const void *data, uint3
|
|||||||
}
|
}
|
||||||
|
|
||||||
void qp_comms_spi_stop(painter_device_t device) {
|
void qp_comms_spi_stop(painter_device_t device) {
|
||||||
struct painter_driver_t * driver = (struct painter_driver_t *)device;
|
painter_driver_t * driver = (painter_driver_t *)device;
|
||||||
struct qp_comms_spi_config_t *comms_config = (struct qp_comms_spi_config_t *)driver->comms_config;
|
qp_comms_spi_config_t *comms_config = (qp_comms_spi_config_t *)driver->comms_config;
|
||||||
spi_stop();
|
spi_stop();
|
||||||
writePinHigh(comms_config->chip_select_pin);
|
writePinHigh(comms_config->chip_select_pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct painter_comms_vtable_t spi_comms_vtable = {
|
const painter_comms_vtable_t spi_comms_vtable = {
|
||||||
.comms_init = qp_comms_spi_init,
|
.comms_init = qp_comms_spi_init,
|
||||||
.comms_start = qp_comms_spi_start,
|
.comms_start = qp_comms_spi_start,
|
||||||
.comms_send = qp_comms_spi_send_data,
|
.comms_send = qp_comms_spi_send_data,
|
||||||
@ -67,8 +69,8 @@ bool qp_comms_spi_dc_reset_init(painter_device_t device) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct painter_driver_t * driver = (struct painter_driver_t *)device;
|
painter_driver_t * driver = (painter_driver_t *)device;
|
||||||
struct qp_comms_spi_dc_reset_config_t *comms_config = (struct qp_comms_spi_dc_reset_config_t *)driver->comms_config;
|
qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config;
|
||||||
|
|
||||||
// Set up D/C as output low, if specified
|
// Set up D/C as output low, if specified
|
||||||
if (comms_config->dc_pin != NO_PIN) {
|
if (comms_config->dc_pin != NO_PIN) {
|
||||||
@ -89,15 +91,15 @@ bool qp_comms_spi_dc_reset_init(painter_device_t device) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t qp_comms_spi_dc_reset_send_data(painter_device_t device, const void *data, uint32_t byte_count) {
|
uint32_t qp_comms_spi_dc_reset_send_data(painter_device_t device, const void *data, uint32_t byte_count) {
|
||||||
struct painter_driver_t * driver = (struct painter_driver_t *)device;
|
painter_driver_t * driver = (painter_driver_t *)device;
|
||||||
struct qp_comms_spi_dc_reset_config_t *comms_config = (struct qp_comms_spi_dc_reset_config_t *)driver->comms_config;
|
qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config;
|
||||||
writePinHigh(comms_config->dc_pin);
|
writePinHigh(comms_config->dc_pin);
|
||||||
return qp_comms_spi_send_data(device, data, byte_count);
|
return qp_comms_spi_send_data(device, data, byte_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qp_comms_spi_dc_reset_send_command(painter_device_t device, uint8_t cmd) {
|
void qp_comms_spi_dc_reset_send_command(painter_device_t device, uint8_t cmd) {
|
||||||
struct painter_driver_t * driver = (struct painter_driver_t *)device;
|
painter_driver_t * driver = (painter_driver_t *)device;
|
||||||
struct qp_comms_spi_dc_reset_config_t *comms_config = (struct qp_comms_spi_dc_reset_config_t *)driver->comms_config;
|
qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config;
|
||||||
writePinLow(comms_config->dc_pin);
|
writePinLow(comms_config->dc_pin);
|
||||||
spi_write(cmd);
|
spi_write(cmd);
|
||||||
}
|
}
|
||||||
@ -118,7 +120,7 @@ void qp_comms_spi_dc_reset_bulk_command_sequence(painter_device_t device, const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct painter_comms_with_command_vtable_t spi_comms_with_dc_vtable = {
|
const painter_comms_with_command_vtable_t spi_comms_with_dc_vtable = {
|
||||||
.base =
|
.base =
|
||||||
{
|
{
|
||||||
.comms_init = qp_comms_spi_dc_reset_init,
|
.comms_init = qp_comms_spi_dc_reset_init,
|
||||||
|
@ -13,36 +13,36 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Base SPI support
|
// Base SPI support
|
||||||
|
|
||||||
struct qp_comms_spi_config_t {
|
typedef struct qp_comms_spi_config_t {
|
||||||
pin_t chip_select_pin;
|
pin_t chip_select_pin;
|
||||||
uint16_t divisor;
|
uint16_t divisor;
|
||||||
bool lsb_first;
|
bool lsb_first;
|
||||||
int8_t mode;
|
int8_t mode;
|
||||||
};
|
} qp_comms_spi_config_t;
|
||||||
|
|
||||||
bool qp_comms_spi_init(painter_device_t device);
|
bool qp_comms_spi_init(painter_device_t device);
|
||||||
bool qp_comms_spi_start(painter_device_t device);
|
bool qp_comms_spi_start(painter_device_t device);
|
||||||
uint32_t qp_comms_spi_send_data(painter_device_t device, const void* data, uint32_t byte_count);
|
uint32_t qp_comms_spi_send_data(painter_device_t device, const void* data, uint32_t byte_count);
|
||||||
void qp_comms_spi_stop(painter_device_t device);
|
void qp_comms_spi_stop(painter_device_t device);
|
||||||
|
|
||||||
extern const struct painter_comms_vtable_t spi_comms_vtable;
|
extern const painter_comms_vtable_t spi_comms_vtable;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// SPI with D/C and RST pins
|
// SPI with D/C and RST pins
|
||||||
|
|
||||||
# ifdef QUANTUM_PAINTER_SPI_DC_RESET_ENABLE
|
# ifdef QUANTUM_PAINTER_SPI_DC_RESET_ENABLE
|
||||||
|
|
||||||
struct qp_comms_spi_dc_reset_config_t {
|
typedef struct qp_comms_spi_dc_reset_config_t {
|
||||||
struct qp_comms_spi_config_t spi_config;
|
qp_comms_spi_config_t spi_config;
|
||||||
pin_t dc_pin;
|
pin_t dc_pin;
|
||||||
pin_t reset_pin;
|
pin_t reset_pin;
|
||||||
};
|
} qp_comms_spi_dc_reset_config_t;
|
||||||
|
|
||||||
void qp_comms_spi_dc_reset_send_command(painter_device_t device, uint8_t cmd);
|
void qp_comms_spi_dc_reset_send_command(painter_device_t device, uint8_t cmd);
|
||||||
uint32_t qp_comms_spi_dc_reset_send_data(painter_device_t device, const void* data, uint32_t byte_count);
|
uint32_t qp_comms_spi_dc_reset_send_data(painter_device_t device, const void* data, uint32_t byte_count);
|
||||||
void qp_comms_spi_dc_reset_bulk_command_sequence(painter_device_t device, const uint8_t* sequence, size_t sequence_len);
|
void qp_comms_spi_dc_reset_bulk_command_sequence(painter_device_t device, const uint8_t* sequence, size_t sequence_len);
|
||||||
|
|
||||||
extern const struct painter_comms_with_command_vtable_t spi_comms_with_dc_vtable;
|
extern const painter_comms_with_command_vtable_t spi_comms_with_dc_vtable;
|
||||||
|
|
||||||
# endif // QUANTUM_PAINTER_SPI_DC_RESET_ENABLE
|
# endif // QUANTUM_PAINTER_SPI_DC_RESET_ENABLE
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user