Merge remote-tracking branch 'qmk/master' into merge-2023-09-08
This commit is contained in:
commit
e390a34128
2
.github/workflows/ci_builds.yml
vendored
2
.github/workflows/ci_builds.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
||||
- name: Disable safe.directory check
|
||||
run : git config --global --add safe.directory '*'
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: ${{ github.event.inputs.branch || github.ref }}
|
||||
|
2
.github/workflows/regen.yml
vendored
2
.github/workflows/regen.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
- name: Disable safe.directory check
|
||||
run : git config --global --add safe.directory '*'
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Run qmk generators
|
||||
run: |
|
||||
|
2
.github/workflows/regen_push.yml
vendored
2
.github/workflows/regen_push.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
- name: Disable safe.directory check
|
||||
run : git config --global --add safe.directory '*'
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Run qmk generators
|
||||
run: |
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -96,7 +96,7 @@ secrets.tar
|
||||
# Python things
|
||||
__pycache__
|
||||
.python-version
|
||||
.venv/
|
||||
.venv
|
||||
|
||||
# Prerequisites for updating ChibiOS
|
||||
/util/fmpp*
|
||||
|
2
Makefile
2
Makefile
@ -328,7 +328,7 @@ define PARSE_TEST
|
||||
ifeq ($$(TEST_NAME),all)
|
||||
MATCHED_TESTS := $$(TEST_LIST)
|
||||
else
|
||||
MATCHED_TESTS := $$(foreach TEST, $$(TEST_LIST),$$(if $$(findstring $$(TEST_NAME), $$(notdir $$(TEST))), $$(TEST),))
|
||||
MATCHED_TESTS := $$(foreach TEST, $$(TEST_LIST),$$(if $$(findstring x$$(TEST_NAME)x, x$$(notdir $$(TEST))x), $$(TEST),))
|
||||
endif
|
||||
$$(foreach TEST,$$(MATCHED_TESTS),$$(eval $$(call BUILD_TEST,$$(TEST),$$(TEST_TARGET))))
|
||||
endef
|
||||
|
@ -17,7 +17,6 @@ $(TEST)_INC := \
|
||||
tests/test_common/common_config.h
|
||||
|
||||
$(TEST)_SRC := \
|
||||
$(TMK_COMMON_SRC) \
|
||||
$(QUANTUM_SRC) \
|
||||
$(SRC) \
|
||||
$(QUANTUM_PATH)/keymap_introspection.c \
|
||||
@ -31,7 +30,7 @@ $(TEST)_SRC := \
|
||||
tests/test_common/test_logger.cpp \
|
||||
$(patsubst $(ROOTDIR)/%,%,$(wildcard $(TEST_PATH)/*.cpp))
|
||||
|
||||
$(TEST)_DEFS := $(TMK_COMMON_DEFS) $(OPT_DEFS) "-DKEYMAP_C=\"keymap.c\""
|
||||
$(TEST)_DEFS := $(OPT_DEFS) "-DKEYMAP_C=\"keymap.c\""
|
||||
|
||||
$(TEST)_CONFIG := $(TEST_PATH)/config.h
|
||||
|
||||
|
@ -27,7 +27,6 @@ QMK_BIN ?= qmk
|
||||
# Set the filename for the final firmware binary
|
||||
KEYBOARD_FILESAFE := $(subst /,_,$(KEYBOARD))
|
||||
TARGET ?= $(KEYBOARD_FILESAFE)_$(KEYMAP)
|
||||
KEYBOARD_OUTPUT := $(BUILD_DIR)/obj_$(KEYBOARD_FILESAFE)
|
||||
|
||||
ifeq ($(strip $(DUMP_CI_METADATA)),yes)
|
||||
$(info CI Metadata: KEYBOARD=$(KEYBOARD))
|
||||
@ -44,7 +43,7 @@ endif
|
||||
# Object files and generated keymap directory
|
||||
# To put object files in current directory, use a dot (.), do NOT make
|
||||
# this an empty or blank macro!
|
||||
KEYMAP_OUTPUT := $(BUILD_DIR)/obj_$(TARGET)
|
||||
INTERMEDIATE_OUTPUT := $(BUILD_DIR)/obj_$(TARGET)
|
||||
|
||||
ifdef SKIP_VERSION
|
||||
OPT_DEFS += -DSKIP_VERSION
|
||||
@ -60,8 +59,8 @@ VERSION_H_FLAGS += --skip-git
|
||||
endif
|
||||
|
||||
# Generate the board's version.h file.
|
||||
$(shell $(QMK_BIN) generate-version-h $(VERSION_H_FLAGS) -q -o $(KEYMAP_OUTPUT)/src/version.h)
|
||||
$(shell python3 util/build_id.py >> $(KEYMAP_OUTPUT)/src/version.h)
|
||||
$(shell $(QMK_BIN) generate-version-h $(VERSION_H_FLAGS) -q -o $(INTERMEDIATE_OUTPUT)/src/version.h)
|
||||
$(shell python3 util/build_id.py >> $(INTERMEDIATE_OUTPUT)/src/version.h)
|
||||
|
||||
# Determine which subfolders exist.
|
||||
KEYBOARD_FOLDER_PATH_1 := $(KEYBOARD)
|
||||
@ -122,7 +121,7 @@ MAIN_KEYMAP_PATH_4 := $(KEYBOARD_PATH_4)/keymaps/$(KEYMAP)
|
||||
MAIN_KEYMAP_PATH_5 := $(KEYBOARD_PATH_5)/keymaps/$(KEYMAP)
|
||||
|
||||
# Pull in rules from info.json
|
||||
INFO_RULES_MK = $(shell $(QMK_BIN) generate-rules-mk --quiet --escape --keyboard $(KEYBOARD) --output $(KEYBOARD_OUTPUT)/src/info_rules.mk)
|
||||
INFO_RULES_MK = $(shell $(QMK_BIN) generate-rules-mk --quiet --escape --keyboard $(KEYBOARD) --output $(INTERMEDIATE_OUTPUT)/src/info_rules.mk)
|
||||
include $(INFO_RULES_MK)
|
||||
|
||||
# Check for keymap.json first, so we can regenerate keymap.c
|
||||
@ -162,28 +161,28 @@ endif
|
||||
|
||||
# Have we found a keymap.json?
|
||||
ifneq ("$(wildcard $(KEYMAP_JSON))", "")
|
||||
KEYMAP_C := $(KEYMAP_OUTPUT)/src/keymap.c
|
||||
KEYMAP_H := $(KEYMAP_OUTPUT)/src/config.h
|
||||
KEYMAP_C := $(INTERMEDIATE_OUTPUT)/src/keymap.c
|
||||
KEYMAP_H := $(INTERMEDIATE_OUTPUT)/src/config.h
|
||||
|
||||
# Load the keymap-level rules.mk if exists
|
||||
-include $(KEYMAP_PATH)/rules.mk
|
||||
|
||||
# Load any rules.mk content from keymap.json
|
||||
INFO_RULES_MK = $(shell $(QMK_BIN) generate-rules-mk --quiet --escape --output $(KEYMAP_OUTPUT)/src/rules.mk $(KEYMAP_JSON))
|
||||
INFO_RULES_MK = $(shell $(QMK_BIN) generate-rules-mk --quiet --escape --output $(INTERMEDIATE_OUTPUT)/src/rules.mk $(KEYMAP_JSON))
|
||||
include $(INFO_RULES_MK)
|
||||
|
||||
# Add rules to generate the keymap files - indentation here is important
|
||||
$(KEYMAP_OUTPUT)/src/keymap.c: $(KEYMAP_JSON)
|
||||
$(INTERMEDIATE_OUTPUT)/src/keymap.c: $(KEYMAP_JSON)
|
||||
@$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD)
|
||||
$(eval CMD=$(QMK_BIN) json2c --quiet --output $(KEYMAP_C) $(KEYMAP_JSON))
|
||||
@$(BUILD_CMD)
|
||||
|
||||
$(KEYMAP_OUTPUT)/src/config.h: $(KEYMAP_JSON)
|
||||
$(INTERMEDIATE_OUTPUT)/src/config.h: $(KEYMAP_JSON)
|
||||
@$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD)
|
||||
$(eval CMD=$(QMK_BIN) generate-config-h --quiet --output $(KEYMAP_H) $(KEYMAP_JSON))
|
||||
@$(BUILD_CMD)
|
||||
|
||||
generated-files: $(KEYMAP_OUTPUT)/src/config.h $(KEYMAP_OUTPUT)/src/keymap.c
|
||||
generated-files: $(INTERMEDIATE_OUTPUT)/src/config.h $(INTERMEDIATE_OUTPUT)/src/keymap.c
|
||||
|
||||
endif
|
||||
|
||||
@ -324,34 +323,34 @@ ifneq ("$(wildcard $(KEYBOARD_PATH_5)/info.json)","")
|
||||
INFO_JSON_FILES += $(KEYBOARD_PATH_5)/info.json
|
||||
endif
|
||||
|
||||
CONFIG_H += $(KEYBOARD_OUTPUT)/src/info_config.h
|
||||
KEYBOARD_SRC += $(KEYBOARD_OUTPUT)/src/default_keyboard.c
|
||||
CONFIG_H += $(INTERMEDIATE_OUTPUT)/src/info_config.h
|
||||
KEYBOARD_SRC += $(INTERMEDIATE_OUTPUT)/src/default_keyboard.c
|
||||
|
||||
$(KEYBOARD_OUTPUT)/src/info_config.h: $(INFO_JSON_FILES)
|
||||
$(INTERMEDIATE_OUTPUT)/src/info_config.h: $(INFO_JSON_FILES)
|
||||
@$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD)
|
||||
$(eval CMD=$(QMK_BIN) generate-config-h --quiet --keyboard $(KEYBOARD) --output $(KEYBOARD_OUTPUT)/src/info_config.h)
|
||||
$(eval CMD=$(QMK_BIN) generate-config-h --quiet --keyboard $(KEYBOARD) --output $(INTERMEDIATE_OUTPUT)/src/info_config.h)
|
||||
@$(BUILD_CMD)
|
||||
|
||||
$(KEYBOARD_OUTPUT)/src/default_keyboard.c: $(INFO_JSON_FILES)
|
||||
$(INTERMEDIATE_OUTPUT)/src/default_keyboard.c: $(INFO_JSON_FILES)
|
||||
@$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD)
|
||||
$(eval CMD=$(QMK_BIN) generate-keyboard-c --quiet --keyboard $(KEYBOARD) --output $(KEYBOARD_OUTPUT)/src/default_keyboard.c)
|
||||
$(eval CMD=$(QMK_BIN) generate-keyboard-c --quiet --keyboard $(KEYBOARD) --output $(INTERMEDIATE_OUTPUT)/src/default_keyboard.c)
|
||||
@$(BUILD_CMD)
|
||||
|
||||
$(KEYBOARD_OUTPUT)/src/default_keyboard.h: $(INFO_JSON_FILES)
|
||||
$(INTERMEDIATE_OUTPUT)/src/default_keyboard.h: $(INFO_JSON_FILES)
|
||||
@$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD)
|
||||
$(eval CMD=$(QMK_BIN) generate-keyboard-h --quiet --keyboard $(KEYBOARD) --include $(FOUND_KEYBOARD_H) --output $(KEYBOARD_OUTPUT)/src/default_keyboard.h)
|
||||
$(eval CMD=$(QMK_BIN) generate-keyboard-h --quiet --keyboard $(KEYBOARD) --include $(FOUND_KEYBOARD_H) --output $(INTERMEDIATE_OUTPUT)/src/default_keyboard.h)
|
||||
@$(BUILD_CMD)
|
||||
|
||||
generated-files: $(KEYBOARD_OUTPUT)/src/info_config.h $(KEYBOARD_OUTPUT)/src/default_keyboard.c $(KEYBOARD_OUTPUT)/src/default_keyboard.h
|
||||
generated-files: $(INTERMEDIATE_OUTPUT)/src/info_config.h $(INTERMEDIATE_OUTPUT)/src/default_keyboard.c $(INTERMEDIATE_OUTPUT)/src/default_keyboard.h
|
||||
|
||||
generated-files: $(KEYMAP_OUTPUT)/src/info_deps.d
|
||||
generated-files: $(INTERMEDIATE_OUTPUT)/src/info_deps.d
|
||||
|
||||
$(KEYMAP_OUTPUT)/src/info_deps.d:
|
||||
$(INTERMEDIATE_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)
|
||||
$(eval CMD=$(QMK_BIN) generate-make-dependencies -kb $(KEYBOARD) -km $(KEYMAP) -o $(INTERMEDIATE_OUTPUT)/src/info_deps.d)
|
||||
@$(BUILD_CMD)
|
||||
|
||||
-include $(KEYMAP_OUTPUT)/src/info_deps.d
|
||||
-include $(INTERMEDIATE_OUTPUT)/src/info_deps.d
|
||||
|
||||
.INTERMEDIATE : generated-files
|
||||
|
||||
@ -424,8 +423,7 @@ VPATH += $(KEYMAP_PATH)
|
||||
VPATH += $(USER_PATH)
|
||||
VPATH += $(KEYBOARD_PATHS)
|
||||
VPATH += $(COMMON_VPATH)
|
||||
VPATH += $(KEYBOARD_OUTPUT)/src
|
||||
VPATH += $(KEYMAP_OUTPUT)/src
|
||||
VPATH += $(INTERMEDIATE_OUTPUT)/src
|
||||
|
||||
include $(BUILDDEFS_PATH)/common_features.mk
|
||||
include $(BUILDDEFS_PATH)/generic_features.mk
|
||||
@ -434,19 +432,17 @@ include $(PLATFORM_PATH)/common.mk
|
||||
|
||||
SRC += $(patsubst %.c,%.clib,$(LIB_SRC))
|
||||
SRC += $(patsubst %.c,%.clib,$(QUANTUM_LIB_SRC))
|
||||
SRC += $(TMK_COMMON_SRC)
|
||||
OPT_DEFS += $(TMK_COMMON_DEFS)
|
||||
EXTRALDFLAGS += $(TMK_COMMON_LDFLAGS)
|
||||
|
||||
-include $(PLATFORM_PATH)/$(PLATFORM_KEY)/bootloader.mk
|
||||
include $(PLATFORM_PATH)/$(PLATFORM_KEY)/platform.mk
|
||||
-include $(PLATFORM_PATH)/$(PLATFORM_KEY)/flash.mk
|
||||
|
||||
ifneq ($(strip $(PROTOCOL)),)
|
||||
include $(TMK_PATH)/protocol/$(strip $(shell echo $(PROTOCOL) | tr '[:upper:]' '[:lower:]')).mk
|
||||
PROTOCOL_KEY = $(strip $(shell echo $(PROTOCOL) | tr '[:upper:]' '[:lower:]'))
|
||||
else
|
||||
include $(TMK_PATH)/protocol/$(PLATFORM_KEY).mk
|
||||
PROTOCOL_KEY = $(PLATFORM_KEY)
|
||||
endif
|
||||
include $(TMK_PATH)/protocol/$(PROTOCOL_KEY)/$(PROTOCOL_KEY).mk
|
||||
|
||||
# Setup definitions based on the selected MCU
|
||||
$(eval $(call add_qmk_prefix_defs,MCU_ORIG,MCU))
|
||||
@ -472,17 +468,14 @@ PROJECT_CONFIG := $(CONFIG_H)
|
||||
CONFIG_H += $(POST_CONFIG_H)
|
||||
ALL_CONFIGS := $(PROJECT_CONFIG) $(CONFIG_H)
|
||||
|
||||
OUTPUTS := $(KEYMAP_OUTPUT) $(KEYBOARD_OUTPUT)
|
||||
$(KEYMAP_OUTPUT)_SRC := $(SRC)
|
||||
$(KEYMAP_OUTPUT)_DEFS := $(OPT_DEFS) \
|
||||
-DQMK_KEYBOARD=\"$(KEYBOARD)\" -DQMK_KEYBOARD_H=\"$(KEYBOARD_OUTPUT)/src/default_keyboard.h\" \
|
||||
-DQMK_KEYMAP=\"$(KEYMAP)\" -DQMK_KEYMAP_H=\"$(KEYMAP).h\" -DQMK_KEYMAP_CONFIG_H=\"$(KEYMAP_PATH)/config.h\"
|
||||
$(KEYMAP_OUTPUT)_INC := $(VPATH) $(EXTRAINCDIRS)
|
||||
$(KEYMAP_OUTPUT)_CONFIG := $(CONFIG_H)
|
||||
$(KEYBOARD_OUTPUT)_SRC := $(PLATFORM_SRC)
|
||||
$(KEYBOARD_OUTPUT)_DEFS := $(PROJECT_DEFS)
|
||||
$(KEYBOARD_OUTPUT)_INC := $(PROJECT_INC)
|
||||
$(KEYBOARD_OUTPUT)_CONFIG := $(PROJECT_CONFIG)
|
||||
OUTPUTS := $(INTERMEDIATE_OUTPUT)
|
||||
$(INTERMEDIATE_OUTPUT)_SRC := $(SRC) $(PLATFORM_SRC)
|
||||
$(INTERMEDIATE_OUTPUT)_DEFS := $(OPT_DEFS) \
|
||||
-DQMK_KEYBOARD=\"$(KEYBOARD)\" -DQMK_KEYBOARD_H=\"$(INTERMEDIATE_OUTPUT)/src/default_keyboard.h\" \
|
||||
-DQMK_KEYMAP=\"$(KEYMAP)\" -DQMK_KEYMAP_H=\"$(KEYMAP).h\" -DQMK_KEYMAP_CONFIG_H=\"$(KEYMAP_PATH)/config.h\" \
|
||||
$(PROJECT_DEFS)
|
||||
$(INTERMEDIATE_OUTPUT)_INC := $(VPATH) $(EXTRAINCDIRS) $(PROJECT_INC)
|
||||
$(INTERMEDIATE_OUTPUT)_CONFIG := $(CONFIG_H) $(PROJECT_CONFIG)
|
||||
|
||||
# Default target.
|
||||
all: build check-size
|
||||
|
@ -317,14 +317,10 @@ ifneq ($(strip $(FLASH_DRIVER)), none)
|
||||
endif
|
||||
|
||||
RGBLIGHT_ENABLE ?= no
|
||||
VALID_RGBLIGHT_TYPES := WS2812 APA102 custom
|
||||
|
||||
ifeq ($(strip $(RGBLIGHT_CUSTOM_DRIVER)), yes)
|
||||
RGBLIGHT_DRIVER ?= custom
|
||||
endif
|
||||
VALID_RGBLIGHT_TYPES := ws2812 apa102 custom
|
||||
|
||||
ifeq ($(strip $(RGBLIGHT_ENABLE)), yes)
|
||||
RGBLIGHT_DRIVER ?= WS2812
|
||||
RGBLIGHT_DRIVER ?= ws2812
|
||||
|
||||
ifeq ($(filter $(RGBLIGHT_DRIVER),$(VALID_RGBLIGHT_TYPES)),)
|
||||
$(call CATASTROPHIC_ERROR,Invalid RGBLIGHT_DRIVER,RGBLIGHT_DRIVER="$(RGBLIGHT_DRIVER)" is not a valid RGB type)
|
||||
@ -338,11 +334,11 @@ ifeq ($(strip $(RGBLIGHT_ENABLE)), yes)
|
||||
RGB_KEYCODES_ENABLE := yes
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(RGBLIGHT_DRIVER)), WS2812)
|
||||
ifeq ($(strip $(RGBLIGHT_DRIVER)), ws2812)
|
||||
WS2812_DRIVER_REQUIRED := yes
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(RGBLIGHT_DRIVER)), APA102)
|
||||
ifeq ($(strip $(RGBLIGHT_DRIVER)), apa102)
|
||||
APA102_DRIVER_REQUIRED := yes
|
||||
endif
|
||||
|
||||
@ -352,8 +348,8 @@ ifeq ($(strip $(RGBLIGHT_ENABLE)), yes)
|
||||
endif
|
||||
|
||||
LED_MATRIX_ENABLE ?= no
|
||||
VALID_LED_MATRIX_TYPES := IS31FL3731 IS31FL3742A IS31FL3743A IS31FL3745 IS31FL3746A CKLED2001 custom
|
||||
# TODO: IS31FL3733 IS31FL3737 IS31FL3741
|
||||
VALID_LED_MATRIX_TYPES := is31fl3731 is31fl3742a is31fl3743a is31fl3745 is31fl3746a ckled2001 custom
|
||||
# TODO: is31fl3733 is31fl3737 is31fl3741
|
||||
|
||||
ifeq ($(strip $(LED_MATRIX_ENABLE)), yes)
|
||||
ifeq ($(filter $(LED_MATRIX_DRIVER),$(VALID_LED_MATRIX_TYPES)),)
|
||||
@ -367,48 +363,49 @@ endif
|
||||
COMMON_VPATH += $(QUANTUM_DIR)/led_matrix
|
||||
COMMON_VPATH += $(QUANTUM_DIR)/led_matrix/animations
|
||||
COMMON_VPATH += $(QUANTUM_DIR)/led_matrix/animations/runners
|
||||
POST_CONFIG_H += $(QUANTUM_DIR)/led_matrix/post_config.h
|
||||
SRC += $(QUANTUM_DIR)/process_keycode/process_backlight.c
|
||||
SRC += $(QUANTUM_DIR)/led_matrix/led_matrix.c
|
||||
SRC += $(QUANTUM_DIR)/led_matrix/led_matrix_drivers.c
|
||||
SRC += $(LIB_PATH)/lib8tion/lib8tion.c
|
||||
CIE1931_CURVE := yes
|
||||
|
||||
ifeq ($(strip $(LED_MATRIX_DRIVER)), IS31FL3731)
|
||||
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3731)
|
||||
OPT_DEFS += -DIS31FL3731 -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31fl3731-simple.c
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(LED_MATRIX_DRIVER)), IS31FL3742A)
|
||||
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3742a)
|
||||
OPT_DEFS += -DIS31FLCOMMON -DIS31FL3742A -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31flcommon.c
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(LED_MATRIX_DRIVER)), IS31FL3743A)
|
||||
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3743a)
|
||||
OPT_DEFS += -DIS31FLCOMMON -DIS31FL3743A -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31flcommon.c
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(LED_MATRIX_DRIVER)), IS31FL3745)
|
||||
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3745)
|
||||
OPT_DEFS += -DIS31FLCOMMON -DIS31FL3745 -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31flcommon.c
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(LED_MATRIX_DRIVER)), IS31FL3746A)
|
||||
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3746a)
|
||||
OPT_DEFS += -DIS31FLCOMMON -DIS31FL3746A -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31flcommon.c
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(LED_MATRIX_DRIVER)), CKLED2001)
|
||||
ifeq ($(strip $(LED_MATRIX_DRIVER)), ckled2001)
|
||||
OPT_DEFS += -DCKLED2001 -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led
|
||||
SRC += ckled2001-simple.c
|
||||
@ -419,7 +416,7 @@ endif
|
||||
|
||||
RGB_MATRIX_ENABLE ?= no
|
||||
|
||||
VALID_RGB_MATRIX_TYPES := AW20216 IS31FL3731 IS31FL3733 IS31FL3736 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 ($(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)
|
||||
@ -432,6 +429,7 @@ endif
|
||||
COMMON_VPATH += $(QUANTUM_DIR)/rgb_matrix
|
||||
COMMON_VPATH += $(QUANTUM_DIR)/rgb_matrix/animations
|
||||
COMMON_VPATH += $(QUANTUM_DIR)/rgb_matrix/animations/runners
|
||||
POST_CONFIG_H += $(QUANTUM_DIR)/rgb_matrix/post_config.h
|
||||
SRC += $(QUANTUM_DIR)/color.c
|
||||
SRC += $(QUANTUM_DIR)/rgb_matrix/rgb_matrix.c
|
||||
SRC += $(QUANTUM_DIR)/rgb_matrix/rgb_matrix_drivers.c
|
||||
@ -439,89 +437,89 @@ endif
|
||||
CIE1931_CURVE := yes
|
||||
RGB_KEYCODES_ENABLE := yes
|
||||
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), AW20216)
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), aw20216)
|
||||
OPT_DEFS += -DAW20216 -DSTM32_SPI -DHAL_USE_SPI=TRUE
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led
|
||||
SRC += aw20216.c
|
||||
QUANTUM_LIB_SRC += spi_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), IS31FL3731)
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3731)
|
||||
OPT_DEFS += -DIS31FL3731 -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31fl3731.c
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), IS31FL3733)
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3733)
|
||||
OPT_DEFS += -DIS31FL3733 -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31fl3733.c
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), IS31FL3736)
|
||||
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
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31fl3737.c
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), IS31FL3741)
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3741)
|
||||
OPT_DEFS += -DIS31FL3741 -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31fl3741.c
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), IS31FL3742A)
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3742a)
|
||||
OPT_DEFS += -DIS31FLCOMMON -DIS31FL3742A -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31flcommon.c
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), IS31FL3743A)
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3743a)
|
||||
OPT_DEFS += -DIS31FLCOMMON -DIS31FL3743A -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31flcommon.c
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), IS31FL3745)
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3745)
|
||||
OPT_DEFS += -DIS31FLCOMMON -DIS31FL3745 -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31flcommon.c
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), IS31FL3746A)
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3746a)
|
||||
OPT_DEFS += -DIS31FLCOMMON -DIS31FL3746A -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31flcommon.c
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), CKLED2001)
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), ckled2001)
|
||||
OPT_DEFS += -DCKLED2001 -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led
|
||||
SRC += ckled2001.c
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), WS2812)
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), ws2812)
|
||||
OPT_DEFS += -DWS2812
|
||||
WS2812_DRIVER_REQUIRED := yes
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), APA102)
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), apa102)
|
||||
OPT_DEFS += -DAPA102
|
||||
APA102_DRIVER_REQUIRED := yes
|
||||
endif
|
||||
@ -565,18 +563,18 @@ ifeq ($(strip $(BACKLIGHT_ENABLE)), yes)
|
||||
endif
|
||||
|
||||
COMMON_VPATH += $(QUANTUM_DIR)/backlight
|
||||
COMMON_VPATH += $(DRIVER_PATH)/backlight
|
||||
SRC += $(QUANTUM_DIR)/backlight/backlight.c
|
||||
SRC += $(QUANTUM_DIR)/process_keycode/process_backlight.c
|
||||
OPT_DEFS += -DBACKLIGHT_ENABLE
|
||||
|
||||
ifeq ($(strip $(BACKLIGHT_DRIVER)), custom)
|
||||
OPT_DEFS += -DBACKLIGHT_CUSTOM_DRIVER
|
||||
else
|
||||
ifneq ($(strip $(BACKLIGHT_DRIVER)), custom)
|
||||
SRC += $(QUANTUM_DIR)/backlight/backlight_driver_common.c
|
||||
ifeq ($(strip $(BACKLIGHT_DRIVER)), pwm)
|
||||
SRC += $(QUANTUM_DIR)/backlight/backlight_$(PLATFORM_KEY).c
|
||||
|
||||
ifeq ($(strip $(BACKLIGHT_DRIVER)), software)
|
||||
SRC += $(DRIVER_PATH)/backlight/backlight_software.c
|
||||
else
|
||||
SRC += $(QUANTUM_DIR)/backlight/backlight_$(strip $(BACKLIGHT_DRIVER)).c
|
||||
SRC += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/backlight_$(strip $(BACKLIGHT_DRIVER)).c
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@ -637,13 +635,13 @@ ifeq ($(strip $(VIAL_ENABLE)), yes)
|
||||
COMBO_ENABLE ?= yes
|
||||
KEY_OVERRIDE_ENABLE ?= yes
|
||||
SRC += $(QUANTUM_DIR)/vial.c
|
||||
EXTRAINCDIRS += $(KEYMAP_OUTPUT)
|
||||
EXTRAINCDIRS += $(INTERMEDIATE_OUTPUT)
|
||||
OPT_DEFS += -DVIAL_ENABLE -DNO_DEBUG -DSERIAL_NUMBER=\"vial:f64c2b3c\"
|
||||
|
||||
$(QUANTUM_DIR)/vial.c: $(KEYMAP_OUTPUT)/vial_generated_keyboard_definition.h
|
||||
$(QUANTUM_DIR)/vial.c: $(INTERMEDIATE_OUTPUT)/vial_generated_keyboard_definition.h
|
||||
|
||||
$(KEYMAP_OUTPUT)/vial_generated_keyboard_definition.h: $(KEYMAP_PATH)/vial.json
|
||||
python3 util/vial_generate_definition.py $(KEYMAP_PATH)/vial.json $(KEYMAP_OUTPUT)/vial_generated_keyboard_definition.h
|
||||
$(INTERMEDIATE_OUTPUT)/vial_generated_keyboard_definition.h: $(KEYMAP_PATH)/vial.json
|
||||
python3 util/vial_generate_definition.py $(KEYMAP_PATH)/vial.json $(INTERMEDIATE_OUTPUT)/vial_generated_keyboard_definition.h
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(VIAL_INSECURE)), yes)
|
||||
@ -757,18 +755,23 @@ ifeq ($(strip $(FNV_ENABLE)), yes)
|
||||
SRC += qmk_fnv_type_validation.c hash_32a.c hash_64a.c
|
||||
endif
|
||||
|
||||
VALID_HAPTIC_DRIVER_TYPES := drv2605l solenoid
|
||||
ifeq ($(strip $(HAPTIC_ENABLE)),yes)
|
||||
ifeq ($(filter $(HAPTIC_DRIVER),$(VALID_HAPTIC_DRIVER_TYPES)),)
|
||||
$(call CATASTROPHIC_ERROR,Invalid HAPTIC_DRIVER,HAPTIC_DRIVER="$(HAPTIC_DRIVER)" is not a valid Haptic driver)
|
||||
else
|
||||
COMMON_VPATH += $(DRIVER_PATH)/haptic
|
||||
|
||||
ifneq ($(filter DRV2605L, $(HAPTIC_DRIVER)), )
|
||||
SRC += DRV2605L.c
|
||||
ifeq ($(strip $(HAPTIC_DRIVER)), drv2605l)
|
||||
SRC += drv2605l.c
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
OPT_DEFS += -DDRV2605L
|
||||
OPT_DEFS += -DHAPTIC_DRV2605L
|
||||
endif
|
||||
|
||||
ifneq ($(filter SOLENOID, $(HAPTIC_DRIVER)), )
|
||||
ifeq ($(strip $(HAPTIC_DRIVER)), solenoid)
|
||||
SRC += solenoid.c
|
||||
OPT_DEFS += -DSOLENOID_ENABLE
|
||||
OPT_DEFS += -DHAPTIC_SOLENOID
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
@ -778,8 +781,8 @@ ifeq ($(strip $(HD44780_ENABLE)), yes)
|
||||
SRC += hd44780.c
|
||||
endif
|
||||
|
||||
VALID_OLED_DRIVER_TYPES := SSD1306 custom
|
||||
OLED_DRIVER ?= SSD1306
|
||||
VALID_OLED_DRIVER_TYPES := custom ssd1306
|
||||
OLED_DRIVER ?= ssd1306
|
||||
VALID_OLED_TRANSPORT_TYPES := i2c spi custom
|
||||
OLED_TRANSPORT ?= i2c
|
||||
ifeq ($(strip $(OLED_ENABLE)), yes)
|
||||
@ -817,13 +820,15 @@ endif
|
||||
ifeq ($(strip $(UCIS_ENABLE)), yes)
|
||||
OPT_DEFS += -DUCIS_ENABLE
|
||||
UNICODE_COMMON := yes
|
||||
SRC += $(QUANTUM_DIR)/process_keycode/process_ucis.c
|
||||
SRC += $(QUANTUM_DIR)/process_keycode/process_ucis.c \
|
||||
$(QUANTUM_DIR)/unicode/ucis.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(UNICODEMAP_ENABLE)), yes)
|
||||
OPT_DEFS += -DUNICODEMAP_ENABLE
|
||||
UNICODE_COMMON := yes
|
||||
SRC += $(QUANTUM_DIR)/process_keycode/process_unicodemap.c
|
||||
SRC += $(QUANTUM_DIR)/process_keycode/process_unicodemap.c \
|
||||
$(QUANTUM_DIR)/unicode/unicodemap.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(UNICODE_ENABLE)), yes)
|
||||
@ -934,7 +939,7 @@ ifeq ($(strip $(USBPD_ENABLE)), yes)
|
||||
endif
|
||||
|
||||
BLUETOOTH_ENABLE ?= no
|
||||
VALID_BLUETOOTH_DRIVER_TYPES := BluefruitLE RN42 custom
|
||||
VALID_BLUETOOTH_DRIVER_TYPES := bluefruit_le custom rn42
|
||||
ifeq ($(strip $(BLUETOOTH_ENABLE)), yes)
|
||||
ifeq ($(filter $(strip $(BLUETOOTH_DRIVER)),$(VALID_BLUETOOTH_DRIVER_TYPES)),)
|
||||
$(call CATASTROPHIC_ERROR,Invalid BLUETOOTH_DRIVER,BLUETOOTH_DRIVER="$(BLUETOOTH_DRIVER)" is not a valid Bluetooth driver type)
|
||||
@ -944,7 +949,7 @@ ifeq ($(strip $(BLUETOOTH_ENABLE)), yes)
|
||||
COMMON_VPATH += $(DRIVER_PATH)/bluetooth
|
||||
SRC += outputselect.c
|
||||
|
||||
ifeq ($(strip $(BLUETOOTH_DRIVER)), BluefruitLE)
|
||||
ifeq ($(strip $(BLUETOOTH_DRIVER)), bluefruit_le)
|
||||
OPT_DEFS += -DBLUETOOTH_BLUEFRUIT_LE -DHAL_USE_SPI=TRUE
|
||||
SRC += $(DRIVER_PATH)/bluetooth/bluetooth.c
|
||||
SRC += $(DRIVER_PATH)/bluetooth/bluefruit_le.cpp
|
||||
@ -952,7 +957,7 @@ ifeq ($(strip $(BLUETOOTH_ENABLE)), yes)
|
||||
QUANTUM_LIB_SRC += spi_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(BLUETOOTH_DRIVER)), RN42)
|
||||
ifeq ($(strip $(BLUETOOTH_DRIVER)), rn42)
|
||||
OPT_DEFS += -DBLUETOOTH_RN42 -DHAL_USE_SERIAL=TRUE
|
||||
SRC += $(DRIVER_PATH)/bluetooth/bluetooth.c
|
||||
SRC += $(DRIVER_PATH)/bluetooth/rn42.c
|
||||
|
@ -176,7 +176,7 @@ MOVE_DEP = mv -f $(patsubst %.o,%.td,$@) $(patsubst %.o,%.d,$@)
|
||||
|
||||
# For a ChibiOS build, ensure that the board files have the hook overrides injected
|
||||
define BOARDSRC_INJECT_HOOKS
|
||||
$(KEYBOARD_OUTPUT)/$(patsubst %.c,%.o,$(patsubst ./%,%,$1)): INIT_HOOK_CFLAGS += -include $(TOP_DIR)/tmk_core/protocol/chibios/init_hooks.h
|
||||
$(INTERMEDIATE_OUTPUT)/$(patsubst %.c,%.o,$(patsubst ./%,%,$1)): INIT_HOOK_CFLAGS += -include $(TOP_DIR)/tmk_core/protocol/chibios/init_hooks.h
|
||||
endef
|
||||
$(foreach LOBJ, $(BOARDSRC), $(eval $(call BOARDSRC_INJECT_HOOKS,$(LOBJ))))
|
||||
|
||||
|
@ -17,7 +17,7 @@ HARDWARE_OPTION_NAMES = \
|
||||
BACKLIGHT_ENABLE \
|
||||
BACKLIGHT_DRIVER \
|
||||
RGBLIGHT_ENABLE \
|
||||
RGBLIGHT_CUSTOM_DRIVER \
|
||||
RGBLIGHT_DRIVER \
|
||||
RGB_MATRIX_ENABLE \
|
||||
RGB_MATRIX_DRIVER \
|
||||
CIE1931_CURVE \
|
||||
@ -60,7 +60,6 @@ OTHER_OPTION_NAMES = \
|
||||
ENCODER_ENABLE_CUSTOM \
|
||||
GERMAN_ENABLE \
|
||||
HAPTIC_ENABLE \
|
||||
HHKB_RN42_ENABLE \
|
||||
ISSI_ENABLE \
|
||||
KEYLOGGER_ENABLE \
|
||||
LCD_BACKLIGHT_ENABLE \
|
||||
|
@ -64,13 +64,12 @@
|
||||
|
||||
// LED Matrix
|
||||
"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_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_TIMEOUT": {"info_key": "led_matrix.timeout", "value_type": "int"},
|
||||
"LED_MATRIX_VAL_STEP": {"info_key": "led_matrix.val_steps", "value_type": "int"},
|
||||
"LED_MATRIX_LED_COUNT": {"info_key": "led_matrix.led_count", "value_type": "int", "to_json": false},
|
||||
|
||||
// LUFA Bootloader
|
||||
"QMK_ESC_INPUT": {"info_key": "qmk_lufa_bootloader.esc_input"},
|
||||
@ -109,6 +108,7 @@
|
||||
"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"},
|
||||
"RGB_MATRIX_LED_COUNT": {"info_key": "rgb_matrix.led_count", "value_type": "int", "to_json": false},
|
||||
|
||||
// RGBLight
|
||||
"RGBLED_NUM": {"info_key": "rgblight.led_count", "value_type": "int"},
|
||||
@ -117,6 +117,7 @@
|
||||
"RGBLIGHT_LAYER_BLINK": {"info_key": "rgblight.layers.blink", "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_LED_MAP": {"info_key": "rgblight.led_map", "value_type": "array.int"},
|
||||
"RGBLIGHT_LIMIT_VAL": {"info_key": "rgblight.max_brightness", "value_type": "int"},
|
||||
"RGBLIGHT_MAX_LAYERS": {"info_key": "rgblight.layers.max", "value_type": "int"},
|
||||
"RGBLIGHT_SAT_STEP": {"info_key": "rgblight.saturation_steps", "value_type": "int"},
|
||||
|
@ -35,6 +35,7 @@
|
||||
"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"},
|
||||
"RGBLIGHT_DRIVER": {"info_key": "rgblight.driver"},
|
||||
"SECURE_ENABLE": {"info_key": "secure.enabled", "value_type": "bool"},
|
||||
"SPLIT_KEYBOARD": {"info_key": "split.enabled", "value_type": "bool"},
|
||||
"SPLIT_TRANSPORT": {"info_key": "split.transport.protocol", "to_c": false},
|
||||
|
@ -1,13 +1,9 @@
|
||||
{
|
||||
// Format for each entry:
|
||||
// "<alias>": {
|
||||
// "target": "<keyboard_folder>",
|
||||
// "layouts": {
|
||||
// "<layout_alias>": "<layout_target>"
|
||||
// }
|
||||
// "target": "<keyboard_folder>"
|
||||
// }
|
||||
//
|
||||
// Both target and layouts are optional.
|
||||
"2_milk": {
|
||||
"target": "spaceman/2_milk"
|
||||
},
|
||||
@ -140,21 +136,6 @@
|
||||
"daisy": {
|
||||
"target": "ktec/daisy"
|
||||
},
|
||||
"doro67/multi": {
|
||||
"layouts": {
|
||||
"LAYOUT_ansi": "LAYOUT_65_ansi_blocker"
|
||||
}
|
||||
},
|
||||
"doro67/regular": {
|
||||
"layouts": {
|
||||
"LAYOUT": "LAYOUT_65_ansi_blocker"
|
||||
}
|
||||
},
|
||||
"doro67/rgb": {
|
||||
"layouts": {
|
||||
"LAYOUT": "LAYOUT_65_ansi_blocker"
|
||||
}
|
||||
},
|
||||
"drakon": {
|
||||
"target": "jagdpietr/drakon"
|
||||
},
|
||||
@ -212,6 +193,12 @@
|
||||
"gmmk/pro/iso": {
|
||||
"target": "gmmk/pro/rev1/iso"
|
||||
},
|
||||
"handwired/dactyl_manuform/3x5_3": {
|
||||
"target": "handwired/dactyl_minidox"
|
||||
},
|
||||
"handwired/dactyl_manuform/6x6_kinesis": {
|
||||
"target": "handwired/dactyl_kinesis"
|
||||
},
|
||||
"handwired/ferris": {
|
||||
"target": "ferris/0_1"
|
||||
},
|
||||
@ -221,6 +208,21 @@
|
||||
"handwired/p1800fl": {
|
||||
"target": "team0110/p1800fl"
|
||||
},
|
||||
"handwired/jscotto/scotto9": {
|
||||
"target": "handwired/scottokeebs/scotto9"
|
||||
},
|
||||
"handwired/jscotto/scotto36": {
|
||||
"target": "handwired/scottokeebs/scotto36"
|
||||
},
|
||||
"handwired/jscotto/scotto40": {
|
||||
"target": "handwired/scottokeebs/scotto40"
|
||||
},
|
||||
"handwired/jscotto/scottocmd": {
|
||||
"target": "handwired/scottokeebs/scottocmd"
|
||||
},
|
||||
"handwired/jscotto/scottostarter": {
|
||||
"target": "handwired/scottokeebs/scottostarter"
|
||||
},
|
||||
"helix/pico/sc/back": {
|
||||
"target": "helix/pico/sc"
|
||||
},
|
||||
@ -264,10 +266,7 @@
|
||||
"target": "keyhive/honeycomb"
|
||||
},
|
||||
"idb_60": {
|
||||
"target": "idb/idb_60",
|
||||
"layouts": {
|
||||
"LAYOUT": "LAYOUT_all"
|
||||
}
|
||||
"target": "idb/idb_60"
|
||||
},
|
||||
"idobo": {
|
||||
"target": "idobao/id75"
|
||||
@ -288,23 +287,20 @@
|
||||
"target": "jones/v03_1"
|
||||
},
|
||||
"kamigakushi": {
|
||||
"target": "jaykeeb/kamigakushi",
|
||||
"layouts": {
|
||||
"LAYOUT": "LAYOUT_65_ansi_blocker_tsangan"
|
||||
}
|
||||
"target": "jaykeeb/kamigakushi"
|
||||
},
|
||||
"katana60": {
|
||||
"target": "rominronin/katana60/rev1"
|
||||
},
|
||||
"kbdfans/kbd67mkiirgb": {
|
||||
"target": "kbdfans/kbd67/mkiirgb",
|
||||
"layouts": {
|
||||
"LAYOUT": "LAYOUT_65_ansi_blocker"
|
||||
}
|
||||
"target": "kbdfans/kbd67/mkiirgb"
|
||||
},
|
||||
"kbdfans/kbd67/mkiirgb": {
|
||||
"target": "kbdfans/kbd67/mkiirgb/v1"
|
||||
},
|
||||
"keebio/chocopad": {
|
||||
"target": "keebio/chocopad/rev1"
|
||||
},
|
||||
"keebio/dsp40": {
|
||||
"target": "keebio/dsp40/rev1"
|
||||
},
|
||||
@ -443,11 +439,6 @@
|
||||
"mschwingen/modelm": {
|
||||
"target": "ibm/model_m/mschwingen"
|
||||
},
|
||||
"noxary/268_2": {
|
||||
"layouts": {
|
||||
"LAYOUT": "LAYOUT_65_ansi_blocker"
|
||||
}
|
||||
},
|
||||
"oddball": {
|
||||
"target": "oddball/v1"
|
||||
},
|
||||
@ -472,11 +463,6 @@
|
||||
"peiorisboards/ixora": {
|
||||
"target": "coarse/ixora"
|
||||
},
|
||||
"percent/canoe": {
|
||||
"layouts": {
|
||||
"LAYOUT_iso": "LAYOUT_65_iso_blocker"
|
||||
}
|
||||
},
|
||||
"plaid": {
|
||||
"target": "dm9records/plaid"
|
||||
},
|
||||
@ -489,21 +475,6 @@
|
||||
"polilla": {
|
||||
"target": "polilla/rev1"
|
||||
},
|
||||
"preonic/rev1": {
|
||||
"layouts": {
|
||||
"LAYOUT_preonic_grid": "LAYOUT_ortho_5x12"
|
||||
}
|
||||
},
|
||||
"preonic/rev2": {
|
||||
"layouts": {
|
||||
"LAYOUT_preonic_grid": "LAYOUT_ortho_5x12"
|
||||
}
|
||||
},
|
||||
"preonic/rev3": {
|
||||
"layouts": {
|
||||
"LAYOUT_preonic_grid": "LAYOUT_ortho_5x12"
|
||||
}
|
||||
},
|
||||
"primekb/prime_l": {
|
||||
"target": "primekb/prime_l/v1"
|
||||
},
|
||||
@ -595,10 +566,7 @@
|
||||
"target": "underscore33/rev1"
|
||||
},
|
||||
"vinta": {
|
||||
"target": "coarse/vinta",
|
||||
"layouts": {
|
||||
"LAYOUT_67_ansi": "LAYOUT_65_ansi_blocker"
|
||||
}
|
||||
"target": "coarse/vinta"
|
||||
},
|
||||
"wasdat": {
|
||||
"target": "maartenwut/wasdat"
|
||||
@ -868,6 +836,9 @@
|
||||
"hecomi/alpha": {
|
||||
"target": "takashiski/hecomi/alpha"
|
||||
},
|
||||
"hfdkb/keyboard_sw/k83":{
|
||||
"target": "inland/kb83"
|
||||
},
|
||||
"hid_liber": {
|
||||
"target": "bpiphany/hid_liber"
|
||||
},
|
||||
|
@ -71,6 +71,8 @@
|
||||
"STM32F446",
|
||||
"STM32G431",
|
||||
"STM32G474",
|
||||
"STM32H723",
|
||||
"STM32H733",
|
||||
"STM32L412",
|
||||
"STM32L422",
|
||||
"STM32L432",
|
||||
@ -146,7 +148,7 @@
|
||||
"properties": {
|
||||
"driver": {
|
||||
"type": "string",
|
||||
"enum": ["BluefruitLE", "RN42", "custom"]
|
||||
"enum": ["bluefruit_le", "custom", "rn42"]
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -175,6 +177,7 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"vibl",
|
||||
"apm32-dfu",
|
||||
"atmel-dfu",
|
||||
"bootloadhid",
|
||||
"caterina",
|
||||
@ -381,8 +384,6 @@
|
||||
},
|
||||
"max_brightness": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
|
||||
"timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"},
|
||||
"hue_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
|
||||
"sat_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
|
||||
"val_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
|
||||
"speed_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
|
||||
"split_count": {
|
||||
@ -476,6 +477,10 @@
|
||||
}
|
||||
},
|
||||
"brightness_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
|
||||
"driver": {
|
||||
"type": "string",
|
||||
"enum": ["apa102", "custom", "ws2812"]
|
||||
},
|
||||
"hue_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
|
||||
"layers": {
|
||||
"type": "object",
|
||||
@ -492,6 +497,11 @@
|
||||
}
|
||||
},
|
||||
"led_count": {"$ref": "qmk.definitions.v1#/unsigned_int"},
|
||||
"led_map": {
|
||||
"type": "array",
|
||||
"minItems": 2,
|
||||
"items": {"$ref": "qmk.definitions.v1#/unsigned_int"}
|
||||
},
|
||||
"max_brightness": {"$ref": "qmk.definitions.v1#/unsigned_int_8"},
|
||||
"pin": {
|
||||
"$ref": "qmk.definitions.v1#/mcu_pin",
|
||||
|
305
docs/ChangeLog/20230827.md
Normal file
305
docs/ChangeLog/20230827.md
Normal file
@ -0,0 +1,305 @@
|
||||
# QMK Breaking Changes - 2023 Aug 27 Changelog
|
||||
|
||||
## Notable Changes :id=notable-changes
|
||||
|
||||
As per last few breaking changes cycles, there have been _a lot_ of 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.
|
||||
|
||||
One thing to note for this release -- `qmk/qmk_firmware` is no longer accepting PRs for keymaps other than for manufacturer-supported keymaps. User keymap workflow has been documented [here](https://docs.qmk.fm/#/newbs) for several years. This change is to progressively reduce the maintenance burden on the project, and to allow us to focus on the core features of QMK.
|
||||
|
||||
Existing user keymaps and userspace areas will likely be relocated/removed in the future -- non-building keymaps and userspace will be first targets, likely during the new breaking changes cycle. We will provide more information on Discord regarding this initiative as it becomes available.
|
||||
|
||||
### RGB Matrix optimizations ([#21134](https://github.com/qmk/qmk_firmware/pull/21134), [#21135](https://github.com/qmk/qmk_firmware/pull/21135)) :id=rgb-matrix-optimizations
|
||||
|
||||
Most RGB Matrix implementations now check whether or not RGB LED data has changed and skip transmission if it hasn't. This was measured to improve scan frequency in cases of static or infrequently-changing colors.
|
||||
|
||||
### Audio optimizations ([#21496](https://github.com/qmk/qmk_firmware/pull/21496), [#21498](https://github.com/qmk/qmk_firmware/pull/21498))
|
||||
|
||||
Some audio code relating to "notes" used `double` datatypes, which are implemented in software floating-point for most ARM microcontrollers. This has been changed to use `float` datatypes instead, which are implemented in hardware floating-point on most ARM microcontrollers. This change increases performance as well as reduces the firmware size by significant number of bytes.
|
||||
|
||||
AVR sees minimal (if any) benefit -- `double` was interpreted as `float` on AVR anyway.
|
||||
|
||||
## Changes Requiring User Action :id=changes-requiring-user-action
|
||||
|
||||
### Updated Keyboard Codebases :id=updated-keyboard-codebases
|
||||
|
||||
| Old Keyboard Name | New Keyboard Name |
|
||||
|---------------------------------------|-------------------------------------|
|
||||
| capsunlocked/cu80/v2_ansi/base | capsunlocked/cu80/v2/ansi |
|
||||
| capsunlocked/cu80/v2_iso/base | capsunlocked/cu80/v2/iso |
|
||||
| handwired/dactyl_manuform/3x5_3 | handwired/dactyl_minidox |
|
||||
| handwired/dactyl_manuform/6x6_kinesis | handwired/dactyl_kinesis |
|
||||
| handwired/jscotto/scotto36 | handwired/scottokeebs/scotto36 |
|
||||
| handwired/jscotto/scotto40 | handwired/scottokeebs/scotto40 |
|
||||
| handwired/jscotto/scotto9 | handwired/scottokeebs/scotto9 |
|
||||
| handwired/jscotto/scottocmd | handwired/scottokeebs/scottocmd |
|
||||
| handwired/jscotto/scottostarter | handwired/scottokeebs/scottostarter |
|
||||
| hfdkb/keyboard_sw/k83 | inland/kb83 |
|
||||
| idb_60 | idb/idb_60 |
|
||||
| kamigakushi | jaykeeb/kamigakushi |
|
||||
| kbdfans/kbd67mkiirgb | kbdfans/kbd67/mkiirgb |
|
||||
| modelh | ibm/model_m/modelh |
|
||||
| vinta | coarse/vinta |
|
||||
|
||||
### Remove encoder in-matrix workaround code ([#20389](https://github.com/qmk/qmk_firmware/pull/20389)) :id=remove-encoder-in-matrix-workaround-code
|
||||
|
||||
Some keyboards "hacked" encoder support into spare slots in the key matrix in order to interoperate with VIA. This workaround is no longer necessary, and the code has been removed. If you have a keyboard that uses this workaround, you will need to update your keymap to use the new [Encoder Map](feature_encoders.md#encoder-map) API instead.
|
||||
|
||||
### Unicodemap keycodes rename ([#21092](https://github.com/qmk/qmk_firmware/pull/21092)) :id=unicodemap-keycodes-rename
|
||||
|
||||
The Unicodemap keycodes have been renamed:
|
||||
|
||||
| Old | New |
|
||||
|-----------|-----------|
|
||||
| `X(i)` | `UM(i)` |
|
||||
| `XP(i,j)` | `UP(i,j)` |
|
||||
|
||||
### Remove old OLED API code ([#21651](https://github.com/qmk/qmk_firmware/pull/21651)) :id=remove-old-oled-api-code
|
||||
|
||||
Old OLED code using `ssd1306.c` `ssd1306.h`, and `SSD1306OLED` and other similar files have been consolidated to use the standard OLED driver. External user keymaps will need to be updated to use the standard OLED driver accordingly.
|
||||
|
||||
### Driver naming consolidation ([#21551](https://github.com/qmk/qmk_firmware/pull/21551), [#21558](https://github.com/qmk/qmk_firmware/pull/21558), [#21580](https://github.com/qmk/qmk_firmware/pull/21580), [#21594](https://github.com/qmk/qmk_firmware/pull/21594), [#21624](https://github.com/qmk/qmk_firmware/pull/21624), [#21710](https://github.com/qmk/qmk_firmware/pull/21710)) :id=driver-naming-consolidation
|
||||
|
||||
In most circumstances this won't affect users -- only keyboard designers with currently-unmerged boards. The only users affected are people who have modified existing keyboards in order to add/modify haptics, lighting, or bluetooth -- and only if the base keyboard did not configure them already. Driver naming has been modified to be lowercase.
|
||||
|
||||
RGBLight (`RGBLIGHT_DRIVER` / `rgblight.driver`):
|
||||
|
||||
| Old | New |
|
||||
|--------|--------|
|
||||
| `WS2812` | `ws2812` |
|
||||
| `APA102` | `apa102` |
|
||||
|
||||
LED Matrix (`LED_MATRIX_DRIVER` / `led_matrix.driver`):
|
||||
|
||||
| Old | New |
|
||||
|-------------|-------------|
|
||||
| `IS31FL3731` | `is31fl3731` |
|
||||
| `IS31FL3742A` | `is31fl3742a` |
|
||||
| `IS31FL3743A` | `is31fl3743a` |
|
||||
| `IS31FL3745` | `is31fl3745` |
|
||||
| `IS31FL3746A` | `is31fl3746a` |
|
||||
| `CKLED2001` | `ckled2001` |
|
||||
|
||||
RGB Matrix (`RGB_MATRIX_DRIVER` / `rgb_matrix.driver`):
|
||||
|
||||
| Old | New |
|
||||
|-------------|-------------|
|
||||
| `AW20216` | `aw20216` |
|
||||
| `IS31FL3731` | `is31fl3731` |
|
||||
| `IS31FL3733` | `is31fl3733` |
|
||||
| `IS31FL3736` | `is31fl3736` |
|
||||
| `IS31FL3737` | `is31fl3737` |
|
||||
| `IS31FL3741` | `is31fl3741` |
|
||||
| `IS31FL3742A` | `is31fl3742a` |
|
||||
| `IS31FL3743A` | `is31fl3743a` |
|
||||
| `IS31FL3745` | `is31fl3745` |
|
||||
| `IS31FL3746A` | `is31fl3746a` |
|
||||
| `CKLED2001` | `ckled2001` |
|
||||
| `WS2812` | `ws2812` |
|
||||
|
||||
OLED (`OLED_DRIVER`):
|
||||
|
||||
| Old | New |
|
||||
|---------|---------|
|
||||
| `SSD1306` | `ssd1306` |
|
||||
|
||||
Haptic (`HAPTIC_DRIVER`):
|
||||
|
||||
| Old | New |
|
||||
|----------|----------|
|
||||
| `DRV2605L` | `drv2605l` |
|
||||
| `SOLENOID` | `solenoid` |
|
||||
|
||||
Bluetooth (`BLUETOOTH_DRIVER` / `bluetooth.driver`):
|
||||
|
||||
| Old | New |
|
||||
|-------------|--------------|
|
||||
| `BluefruitLE` | `bluefruit_le` |
|
||||
| `RN42` | `rn42` |
|
||||
|
||||
## Full changelist :id=full-changelist
|
||||
|
||||
Core:
|
||||
* On-each-release tap dance function ([#20255](https://github.com/qmk/qmk_firmware/pull/20255))
|
||||
* Send a dummy keycode to neutralize flashing modifiers in retro tap and key overrides ([#20992](https://github.com/qmk/qmk_firmware/pull/20992))
|
||||
* Adds a way to separate tab from AUTO_SHIFT_SPECIAL. ([#20996](https://github.com/qmk/qmk_firmware/pull/20996))
|
||||
* [Enhancement] More info on `apply_autocorrect` ([#21056](https://github.com/qmk/qmk_firmware/pull/21056))
|
||||
* Remove quantum/keymap.h ([#21086](https://github.com/qmk/qmk_firmware/pull/21086))
|
||||
* Unicodemap keycodes rename ([#21092](https://github.com/qmk/qmk_firmware/pull/21092))
|
||||
* Merge upstream uf2conv.py changes ([#21107](https://github.com/qmk/qmk_firmware/pull/21107))
|
||||
* Add a dynamic_macro_stop_recording(void) function. ([#21108](https://github.com/qmk/qmk_firmware/pull/21108))
|
||||
* platforms: chibios: wait: only define the frequency ([#21115](https://github.com/qmk/qmk_firmware/pull/21115))
|
||||
* [Enhancement] Decouple autocorrect logic ([#21116](https://github.com/qmk/qmk_firmware/pull/21116))
|
||||
* Optimisation - Add RGB LED colour set check in drivers ([#21134](https://github.com/qmk/qmk_firmware/pull/21134))
|
||||
* RGB matrix ws2812 update ([#21135](https://github.com/qmk/qmk_firmware/pull/21135))
|
||||
* Pixel rain: Refactor the rain light decision operator ([#21139](https://github.com/qmk/qmk_firmware/pull/21139))
|
||||
* Use unsigned integer for kinetic speed ([#21151](https://github.com/qmk/qmk_firmware/pull/21151))
|
||||
* Reset `matrix_need_update` properly in eager debouncing algorithms ([#21154](https://github.com/qmk/qmk_firmware/pull/21154))
|
||||
* Refactor kinetic mouse key feature ([#21164](https://github.com/qmk/qmk_firmware/pull/21164))
|
||||
* RGB Matrix limit basic indicators to the last render ([#21169](https://github.com/qmk/qmk_firmware/pull/21169))
|
||||
* dynamic keymap: Rely on introspection to handle OOB access. ([#21247](https://github.com/qmk/qmk_firmware/pull/21247))
|
||||
* add VIA support for LED Matrix ([#21281](https://github.com/qmk/qmk_firmware/pull/21281))
|
||||
* Refactor times inverse of sqrt 2 calculation ([#21293](https://github.com/qmk/qmk_firmware/pull/21293))
|
||||
* Move protocol makefiles into their respective folders ([#21332](https://github.com/qmk/qmk_firmware/pull/21332))
|
||||
* Remove use of __flash within LED drivers ([#21343](https://github.com/qmk/qmk_firmware/pull/21343))
|
||||
* STM32H723 support ([#21352](https://github.com/qmk/qmk_firmware/pull/21352))
|
||||
* Remove CORTEX_ENABLE_WFI_IDLE from keyboards. ([#21353](https://github.com/qmk/qmk_firmware/pull/21353))
|
||||
* Get rid of `USB_LED_KANA` and `USB_LED_COMPOSE` ([#21366](https://github.com/qmk/qmk_firmware/pull/21366))
|
||||
* Minor board clean-up after #19780 ([#21391](https://github.com/qmk/qmk_firmware/pull/21391))
|
||||
* Get rid of `USB_LED_SCROLL_LOCK` ([#21405](https://github.com/qmk/qmk_firmware/pull/21405))
|
||||
* Get rid of `USB_LED_NUM_LOCK` ([#21424](https://github.com/qmk/qmk_firmware/pull/21424))
|
||||
* Simplify audio_duration_to_ms() and audio_ms_to_duration(), reduce firmware size by a few bytes. ([#21427](https://github.com/qmk/qmk_firmware/pull/21427))
|
||||
* Allow key override to respect weak mods caused by caps word ([#21434](https://github.com/qmk/qmk_firmware/pull/21434))
|
||||
* Get rid of `USB_LED_CAPS_LOCK` ([#21436](https://github.com/qmk/qmk_firmware/pull/21436))
|
||||
* tmk_core: remove direct `quantum.h` includes ([#21465](https://github.com/qmk/qmk_firmware/pull/21465))
|
||||
* bootmagic mods covering the case when swapped mods are pressed at the same time (#21320) ([#21472](https://github.com/qmk/qmk_firmware/pull/21472))
|
||||
* drivers: remove direct `quantum.h` includes ([#21473](https://github.com/qmk/qmk_firmware/pull/21473))
|
||||
* debounce: remove direct `quantum.h` includes ([#21480](https://github.com/qmk/qmk_firmware/pull/21480))
|
||||
* keymap_extras: remove direct `quantum.h` includes ([#21485](https://github.com/qmk/qmk_firmware/pull/21485))
|
||||
* process_keycode: remove direct `quantum.h` includes ([#21486](https://github.com/qmk/qmk_firmware/pull/21486))
|
||||
* Add MOUSEKEY_WHEEL_DELTA documentation ([#21493](https://github.com/qmk/qmk_firmware/pull/21493))
|
||||
* Reduce needless precision in audio note frequency calculation ([#21496](https://github.com/qmk/qmk_firmware/pull/21496))
|
||||
* Remove needless precision in additive DAC sample generation ([#21498](https://github.com/qmk/qmk_firmware/pull/21498))
|
||||
* quantum: remove direct `quantum.h` includes ([#21507](https://github.com/qmk/qmk_firmware/pull/21507))
|
||||
* process_combo: restore wait.h header ([#21514](https://github.com/qmk/qmk_firmware/pull/21514))
|
||||
* Eliminate `TMK_COMMON_*` in makefiles ([#21517](https://github.com/qmk/qmk_firmware/pull/21517))
|
||||
* backlight: split AVR PWM and timer drivers ([#21540](https://github.com/qmk/qmk_firmware/pull/21540))
|
||||
* haptic: naming cleanups ([#21551](https://github.com/qmk/qmk_firmware/pull/21551))
|
||||
* rgblight: driver selection cleanups ([#21558](https://github.com/qmk/qmk_firmware/pull/21558))
|
||||
* LED Matrix: driver naming cleanups ([#21580](https://github.com/qmk/qmk_firmware/pull/21580))
|
||||
* Unify MIDI note calculation with the audio feature (from #21496) ([#21588](https://github.com/qmk/qmk_firmware/pull/21588))
|
||||
* Allow the user to select a single tone for the additive DAC ([#21591](https://github.com/qmk/qmk_firmware/pull/21591))
|
||||
* RGB Matrix: driver naming cleanups ([#21594](https://github.com/qmk/qmk_firmware/pull/21594))
|
||||
* Raw HID: documentation improvements ([#21596](https://github.com/qmk/qmk_firmware/pull/21596))
|
||||
* Unicode: move keycode aliases to a separate header ([#21613](https://github.com/qmk/qmk_firmware/pull/21613))
|
||||
* Bluetooth: driver naming cleanups ([#21624](https://github.com/qmk/qmk_firmware/pull/21624))
|
||||
* Remove old OLED API code ([#21651](https://github.com/qmk/qmk_firmware/pull/21651))
|
||||
* haptic: further naming cleanups ([#21682](https://github.com/qmk/qmk_firmware/pull/21682))
|
||||
* Simplfy RGB/LED matrix effect logic ([#21703](https://github.com/qmk/qmk_firmware/pull/21703))
|
||||
* OLED: driver naming cleanups ([#21710](https://github.com/qmk/qmk_firmware/pull/21710))
|
||||
|
||||
CLI:
|
||||
* Add *_MATRIX_LED_COUNT generation/validation ([#19515](https://github.com/qmk/qmk_firmware/pull/19515))
|
||||
* Revert "Add *_MATRIX_LED_COUNT generation/validation" ([#21109](https://github.com/qmk/qmk_firmware/pull/21109))
|
||||
* Add *_MATRIX_LED_COUNT generation ([#21110](https://github.com/qmk/qmk_firmware/pull/21110))
|
||||
* feat, docs: WB32 flashing ([#21217](https://github.com/qmk/qmk_firmware/pull/21217))
|
||||
* Improve error messages when layout key matrix row/col is OOB ([#21640](https://github.com/qmk/qmk_firmware/pull/21640))
|
||||
|
||||
Submodule updates:
|
||||
* Update ChibiOS-Contrib ([#21553](https://github.com/qmk/qmk_firmware/pull/21553))
|
||||
|
||||
Keyboards:
|
||||
* Add support for Rastersoft MiniTKL ([#20230](https://github.com/qmk/qmk_firmware/pull/20230))
|
||||
* Remove encoder in-matrix workaround code ([#20389](https://github.com/qmk/qmk_firmware/pull/20389))
|
||||
* Revamp `dactyl_manuform` readme.md ([#20395](https://github.com/qmk/qmk_firmware/pull/20395))
|
||||
* added hackpad keyboard ([#20402](https://github.com/qmk/qmk_firmware/pull/20402))
|
||||
* Add `handwired/dactyl_cc` keyboard ([#20517](https://github.com/qmk/qmk_firmware/pull/20517))
|
||||
* Add Mino Plus Hotswap ([#20534](https://github.com/qmk/qmk_firmware/pull/20534))
|
||||
* Move kb83 keyboard. ([#20761](https://github.com/qmk/qmk_firmware/pull/20761))
|
||||
* Rename `dactyl_manuform` variant `3x5_3` ([#21015](https://github.com/qmk/qmk_firmware/pull/21015))
|
||||
* Update `k34` layout to `split_3x5_2` ([#21046](https://github.com/qmk/qmk_firmware/pull/21046))
|
||||
* giabalanai keymaps: transpose added ([#21054](https://github.com/qmk/qmk_firmware/pull/21054))
|
||||
* Move `RGBLIGHT_SLEEP` to data driven ([#21072](https://github.com/qmk/qmk_firmware/pull/21072))
|
||||
* update layouts of `dactyl_manuform/4x5_5` ([#21094](https://github.com/qmk/qmk_firmware/pull/21094))
|
||||
* Move `RGBLIGHT_LED_MAP` to data driven ([#21095](https://github.com/qmk/qmk_firmware/pull/21095))
|
||||
* Move `RGBLED_SPLIT` to data driven ([#21113](https://github.com/qmk/qmk_firmware/pull/21113))
|
||||
* Update `dactyl_promicro` readme ([#21144](https://github.com/qmk/qmk_firmware/pull/21144))
|
||||
* Delete jscotto directory ([#21157](https://github.com/qmk/qmk_firmware/pull/21157))
|
||||
* correct and modernise `dactyl_manuform/6x7` variant ([#21176](https://github.com/qmk/qmk_firmware/pull/21176))
|
||||
* Move `RGBLIGHT_SPLIT` to data driven ([#21190](https://github.com/qmk/qmk_firmware/pull/21190))
|
||||
* Minor amendment to `bcat` userspace to prevent build failure ([#21205](https://github.com/qmk/qmk_firmware/pull/21205))
|
||||
* FJLabs Swordfish Layout Macro Refactor ([#21234](https://github.com/qmk/qmk_firmware/pull/21234))
|
||||
* Add skyloong/Dt40 keyboard ([#21237](https://github.com/qmk/qmk_firmware/pull/21237))
|
||||
* `dactyl_manuform/6x7` correction ([#21240](https://github.com/qmk/qmk_firmware/pull/21240))
|
||||
* Amend `ryanbaekr` boards by pin definitions ([#21248](https://github.com/qmk/qmk_firmware/pull/21248))
|
||||
* EC Pro X JIS Layout Touch-Up ([#21260](https://github.com/qmk/qmk_firmware/pull/21260))
|
||||
* Eason Aeroboard Refactor ([#21271](https://github.com/qmk/qmk_firmware/pull/21271))
|
||||
* Move `RGBLED_NUM` to data driven ([#21278](https://github.com/qmk/qmk_firmware/pull/21278))
|
||||
* Remove default `TAPPING_TERM` from keyboard config.h ([#21284](https://github.com/qmk/qmk_firmware/pull/21284))
|
||||
* Move `RGBLIGHT_HUE/SAT/VAL_STEP` to data driven ([#21292](https://github.com/qmk/qmk_firmware/pull/21292))
|
||||
* Move `TAPPING_TERM` to data driven ([#21296](https://github.com/qmk/qmk_firmware/pull/21296))
|
||||
* Modernize, correct, and uniform `dactyl_manuform` variant `5x6_68` ([#21299](https://github.com/qmk/qmk_firmware/pull/21299))
|
||||
* rename and modernise `dactyl_manuform/6x6_kinesis` ([#21302](https://github.com/qmk/qmk_firmware/pull/21302))
|
||||
* ProtoTypist PT-60 Refactor ([#21322](https://github.com/qmk/qmk_firmware/pull/21322))
|
||||
* ProtoTypist PT-80 Refactor ([#21325](https://github.com/qmk/qmk_firmware/pull/21325))
|
||||
* add jels60v2 support ([#21337](https://github.com/qmk/qmk_firmware/pull/21337))
|
||||
* Move `RGB_MATRIX_HUE/SAT/VAL/SPD_STEP` to data driven ([#21354](https://github.com/qmk/qmk_firmware/pull/21354))
|
||||
* Move `TAPPING_TOGGLE` to data driven ([#21360](https://github.com/qmk/qmk_firmware/pull/21360))
|
||||
* Move `TAP_CODE_DELAY` to data driven ([#21363](https://github.com/qmk/qmk_firmware/pull/21363))
|
||||
* gmmk/pro: Turn off RGB when suspended ([#21370](https://github.com/qmk/qmk_firmware/pull/21370))
|
||||
* Move miscellaneous defines to data driven ([#21382](https://github.com/qmk/qmk_firmware/pull/21382))
|
||||
* kyria: remove `LAYOUT_stack` ([#21384](https://github.com/qmk/qmk_firmware/pull/21384))
|
||||
* Reduce `keebio/bamfk1:via` firmware size ([#21432](https://github.com/qmk/qmk_firmware/pull/21432))
|
||||
* Refactor `capsunlocked/cu80/v2` ([#21454](https://github.com/qmk/qmk_firmware/pull/21454))
|
||||
* Mechlovin Zed65 rev1 Develop Touch-Up ([#21476](https://github.com/qmk/qmk_firmware/pull/21476))
|
||||
* Add PW88 keyboard ([#21482](https://github.com/qmk/qmk_firmware/pull/21482))
|
||||
* Prepare ymdk/ymd75 for rev4 ([#21484](https://github.com/qmk/qmk_firmware/pull/21484))
|
||||
* Move `DEBOUNCE_TYPE` to data driven ([#21489](https://github.com/qmk/qmk_firmware/pull/21489))
|
||||
* aleblazer/zodiark:via: Disable two RGB effects ([#21495](https://github.com/qmk/qmk_firmware/pull/21495))
|
||||
* Spruce up `dactyl_lightcycle` and `dactyl_maximus` layouts ([#21519](https://github.com/qmk/qmk_firmware/pull/21519))
|
||||
* Amend layout and matrix positions for `dactyl_cc` ([#21523](https://github.com/qmk/qmk_firmware/pull/21523))
|
||||
* moved model h controller under ibm/model_m ([#21526](https://github.com/qmk/qmk_firmware/pull/21526))
|
||||
* tominabox1/le_chiffre refactor pt 1 ([#21567](https://github.com/qmk/qmk_firmware/pull/21567))
|
||||
* Update ERA65 PCB ([#21592](https://github.com/qmk/qmk_firmware/pull/21592))
|
||||
* Update `usb.`* for dactyl_cc ([#21612](https://github.com/qmk/qmk_firmware/pull/21612))
|
||||
* Kintwin controller for kinesis keyboard, split layout ([#21614](https://github.com/qmk/qmk_firmware/pull/21614))
|
||||
* Add STM32f3 Discovery onekey ([#21625](https://github.com/qmk/qmk_firmware/pull/21625))
|
||||
* Automata02 Alisaie Develop Touch-Up ([#21630](https://github.com/qmk/qmk_firmware/pull/21630))
|
||||
* Move RGBLight animations to data driven ([#21635](https://github.com/qmk/qmk_firmware/pull/21635))
|
||||
* Refactoring entirely Caticorn PCB ([#21644](https://github.com/qmk/qmk_firmware/pull/21644))
|
||||
* AMJKeyboard AMJ84 Develop Touch-Up ([#21645](https://github.com/qmk/qmk_firmware/pull/21645))
|
||||
* Remove layout aliases from keyboard_aliases.hjson ([#21658](https://github.com/qmk/qmk_firmware/pull/21658))
|
||||
* kikoslab/kl90: Remove invalid config option ([#21708](https://github.com/qmk/qmk_firmware/pull/21708))
|
||||
* Remove more legacy config.h options ([#21709](https://github.com/qmk/qmk_firmware/pull/21709))
|
||||
* add willoucom/keypad ([#21714](https://github.com/qmk/qmk_firmware/pull/21714))
|
||||
* Tidy up encoder in matrix references ([#21718](https://github.com/qmk/qmk_firmware/pull/21718))
|
||||
* Add city42 ([#21727](https://github.com/qmk/qmk_firmware/pull/21727))
|
||||
* feat: add squigglybob splitkb kyria rev2 keymap ([#21751](https://github.com/qmk/qmk_firmware/pull/21751))
|
||||
* Align SENSE75 with recent Drop additions ([#21757](https://github.com/qmk/qmk_firmware/pull/21757))
|
||||
|
||||
Keyboard fixes:
|
||||
* fix `scheikled` keymap for `dactyl_manuform/4x6` ([#21206](https://github.com/qmk/qmk_firmware/pull/21206))
|
||||
* Fixup `dekunukem/duckypad` ([#21298](https://github.com/qmk/qmk_firmware/pull/21298))
|
||||
* Fixup `nightly_boards/n40_o` ([#21307](https://github.com/qmk/qmk_firmware/pull/21307))
|
||||
* Fix `rate/pistachio_pro:via` ([#21339](https://github.com/qmk/qmk_firmware/pull/21339))
|
||||
* Fix encoder map declarations ([#21435](https://github.com/qmk/qmk_firmware/pull/21435))
|
||||
* jones/v1: fix layout offset and disable audio on via keymap ([#21468](https://github.com/qmk/qmk_firmware/pull/21468))
|
||||
* Fix backlight support for some boards ([#21554](https://github.com/qmk/qmk_firmware/pull/21554))
|
||||
* kinesis: remove stacked split layouts ([#21569](https://github.com/qmk/qmk_firmware/pull/21569))
|
||||
* Fix layout offsets for a handful of boards ([#21636](https://github.com/qmk/qmk_firmware/pull/21636))
|
||||
* doio/kb38: fix layout ([#21704](https://github.com/qmk/qmk_firmware/pull/21704))
|
||||
* Fix drop/shift/v2 compilation ([#21800](https://github.com/qmk/qmk_firmware/pull/21800))
|
||||
* Fix keyboards with old RGB driver names ([#21815](https://github.com/qmk/qmk_firmware/pull/21815))
|
||||
* Fix keyboards with old RGB driver names ([#21817](https://github.com/qmk/qmk_firmware/pull/21817))
|
||||
|
||||
Others:
|
||||
* Rework info.json reference ([#21324](https://github.com/qmk/qmk_firmware/pull/21324))
|
||||
* Enable auto-merge of develop to riot ([#21389](https://github.com/qmk/qmk_firmware/pull/21389))
|
||||
|
||||
Bugs:
|
||||
* Fix non-functional S3 wakeup / resume from suspense ([#19780](https://github.com/qmk/qmk_firmware/pull/19780))
|
||||
* [Bugfix] Check `NULL` pointers on QP ([#20481](https://github.com/qmk/qmk_firmware/pull/20481))
|
||||
* Fix PS2_MOUSE_INVERT_BUTTONS ([#20646](https://github.com/qmk/qmk_firmware/pull/20646))
|
||||
* Fix backlight sync on suspend_power_down for split keyboards ([#21079](https://github.com/qmk/qmk_firmware/pull/21079))
|
||||
* Consolidate `KEYBOARD_OUTPUT`+`KEYMAP_OUTPUT`=>`INTERMEDIATE_OUTPUT` ([#21272](https://github.com/qmk/qmk_firmware/pull/21272))
|
||||
* Chibios USB: Take into account if host wants remote wakeup or not ([#21287](https://github.com/qmk/qmk_firmware/pull/21287))
|
||||
* Fix anchor IDs for some API references ([#21345](https://github.com/qmk/qmk_firmware/pull/21345))
|
||||
* Pixel fractal: Set minimum middle column value ([#21365](https://github.com/qmk/qmk_firmware/pull/21365))
|
||||
* Fix ili9xxx inversion opcode entry ([#21422](https://github.com/qmk/qmk_firmware/pull/21422))
|
||||
* Relocate backlight drivers ([#21444](https://github.com/qmk/qmk_firmware/pull/21444))
|
||||
* Fixup STM32-DFU ([#21447](https://github.com/qmk/qmk_firmware/pull/21447))
|
||||
* keycode aliases: work around ChibiOS ch.h include guard ([#21497](https://github.com/qmk/qmk_firmware/pull/21497))
|
||||
* Fix compilation error when Split Watchdog enabled ([#21543](https://github.com/qmk/qmk_firmware/pull/21543))
|
||||
* Revert " Fix compilation error when Split Watchdog enabled" ([#21572](https://github.com/qmk/qmk_firmware/pull/21572))
|
||||
* quantum.h: clean up process_keycode includes ([#21579](https://github.com/qmk/qmk_firmware/pull/21579))
|
||||
* Fix stuck note with square wave in additive DAC ([#21589](https://github.com/qmk/qmk_firmware/pull/21589))
|
||||
* [Fix] USB HID tests compliance ([#21626](https://github.com/qmk/qmk_firmware/pull/21626))
|
||||
* Fix Dynamic Macro Compilation for avr-gcc 5.4.0 + Linux ([#21653](https://github.com/qmk/qmk_firmware/pull/21653))
|
||||
* Unicode, Unicodemap and UCIS refactor ([#21659](https://github.com/qmk/qmk_firmware/pull/21659))
|
||||
* Audio: Don't play the first note of zero-note melodies ([#21661](https://github.com/qmk/qmk_firmware/pull/21661))
|
||||
* Fix mouse-key spamming empty reports ([#21663](https://github.com/qmk/qmk_firmware/pull/21663))
|
||||
* Restore usb suspend wakeup delay ([#21676](https://github.com/qmk/qmk_firmware/pull/21676))
|
||||
* Fix compilation error for APA on ChibiOS ([#21773](https://github.com/qmk/qmk_firmware/pull/21773))
|
||||
* fix: restore rgb matrix indicators to jellybean_raindrops animation ([#21792](https://github.com/qmk/qmk_firmware/pull/21792))
|
||||
* Remove `led_matrix.hue_steps` and `led_matrix.sat_steps` from schema ([#21827](https://github.com/qmk/qmk_firmware/pull/21827))
|
||||
* Revert changes to ChibiOS Suspend Code ([#21830](https://github.com/qmk/qmk_firmware/pull/21830))
|
||||
* Add "apm32-dfu" in keyboard.jsonschema ([#21842](https://github.com/qmk/qmk_firmware/pull/21842))
|
@ -139,7 +139,7 @@
|
||||
* Breaking Changes
|
||||
* [Overview](breaking_changes.md)
|
||||
* [My Pull Request Was Flagged](breaking_changes_instructions.md)
|
||||
* [Most Recent ChangeLog](ChangeLog/20230528.md "QMK v0.21.0 - 2023 May 28")
|
||||
* [Most Recent ChangeLog](ChangeLog/20230827.md "QMK v0.22.0 - 2023 Aug 27")
|
||||
* [Past Breaking Changes](breaking_changes_history.md)
|
||||
|
||||
* C Development
|
||||
|
@ -10,25 +10,25 @@ Practically, this means QMK merges the `develop` branch into the `master` branch
|
||||
|
||||
## What has been included in past Breaking Changes?
|
||||
|
||||
* [2023 Aug 27](ChangeLog/20230827.md)
|
||||
* [2023 May 28](ChangeLog/20230528.md)
|
||||
* [2023 Feb 26](ChangeLog/20230226.md)
|
||||
* [2022 Nov 26](ChangeLog/20221126.md)
|
||||
* [Older Breaking Changes](breaking_changes_history.md)
|
||||
|
||||
## When is the next Breaking Change?
|
||||
|
||||
The next Breaking Change is scheduled for August 27, 2023.
|
||||
The next Breaking Change is scheduled for November 26, 2023.
|
||||
|
||||
### Important Dates
|
||||
|
||||
* 2023 May 28 - `develop` is tagged with a new release version. Each push to `master` is subsequently merged to `develop` by GitHub actions.
|
||||
* 2023 Jul 30 - `develop` closed to new PRs.
|
||||
* 2023 Jul 30 - Call for testers.
|
||||
* 2023 Aug 13 - Last day for merges -- after this point `develop` is locked for testing and accepts only bugfixes
|
||||
* 2023 Aug 20 - `develop` is locked, only critical bugfix PRs merged.
|
||||
* 2023 Aug 25 - `master` is locked, no PRs merged.
|
||||
* 2023 Aug 27 - Merge `develop` to `master`.
|
||||
* 2023 Aug 27 - `master` is unlocked. PRs can be merged again.
|
||||
* 2023 Aug 27 - `develop` is tagged with a new release version. Each push to `master` is subsequently merged to `develop` by GitHub actions.
|
||||
* 2023 Oct 29 - `develop` closed to new PRs.
|
||||
* 2023 Oct 29 - Call for testers.
|
||||
* 2023 Nov 5 - Last day for merges -- after this point `develop` is locked for testing and accepts only bugfixes
|
||||
* 2023 Nov 19 - `develop` is locked, only critical bugfix PRs merged.
|
||||
* 2023 Nov 23 - `master` is locked, no PRs merged.
|
||||
* 2023 Nov 26 - Merge `develop` to `master`.
|
||||
* 2023 Nov 26 - `master` is unlocked. PRs can be merged again.
|
||||
|
||||
## What changes will be included?
|
||||
|
||||
@ -48,7 +48,7 @@ Criteria for acceptance:
|
||||
|
||||
Strongly suggested:
|
||||
|
||||
* The PR has a ChangeLog file describing the changes under `<qmk_firmware>/docs/Changelog/20230827`.
|
||||
* The PR has a ChangeLog file describing the changes under `<qmk_firmware>/docs/Changelog/20231126`.
|
||||
* 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.
|
||||
|
||||
@ -119,6 +119,7 @@ This happens immediately after the previous `develop` branch is merged to `maste
|
||||
* `git commit -m 'Branch point for <DATE> Breaking Change'`
|
||||
* `git tag breakpoint_<YYYY>_<MM>_<DD>`
|
||||
* `git push upstream breakpoint_<YYYY>_<MM>_<DD>`
|
||||
* `git push upstream develop`
|
||||
|
||||
* All submodules under `lib` now need to be checked against their QMK-based forks:
|
||||
* `git submodule foreach git log -n1`
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
This page links to all previous changelogs from the QMK Breaking Changes process.
|
||||
|
||||
* [2023 Aug 27](ChangeLog/20230827.md) - version 0.22.0
|
||||
* [2023 May 28](ChangeLog/20230528.md) - version 0.21.0
|
||||
* [2023 Feb 26](ChangeLog/20230226.md) - version 0.20.0
|
||||
* [2022 Nov 26](ChangeLog/20221126.md) - version 0.19.0
|
||||
|
@ -8,8 +8,8 @@ Most of our style is pretty easy to pick up on, but right now it's not entirely
|
||||
* Closing Brace: Lined up with the first character of the statement that opens the block
|
||||
* Else If: Place the closing brace at the beginning of the line and the next opening brace at the end of the same line.
|
||||
* Optional Braces: Always include optional braces.
|
||||
* Good: if (condition) { return false; }
|
||||
* Bad: if (condition) return false;
|
||||
* Good: `if (condition) { return false; }`
|
||||
* Bad: `if (condition) return false;`
|
||||
* We encourage use of C style comments: `/* */`
|
||||
* Think of them as a story describing the feature
|
||||
* Use them liberally to explain why particular decisions were made.
|
||||
|
@ -43,6 +43,8 @@ You can also use any ARM chip with USB that [ChibiOS](https://www.chibios.org) s
|
||||
* [STM32F446](https://www.st.com/en/microcontrollers-microprocessors/stm32f446.html)
|
||||
* [STM32G431](https://www.st.com/en/microcontrollers-microprocessors/stm32g4x1.html)
|
||||
* [STM32G474](https://www.st.com/en/microcontrollers-microprocessors/stm32g4x4.html)
|
||||
* [STM32H723](https://www.st.com/en/microcontrollers-microprocessors/stm32h723-733.html)
|
||||
* [STM32H733](https://www.st.com/en/microcontrollers-microprocessors/stm32h723-733.html)
|
||||
* [STM32L412](https://www.st.com/en/microcontrollers-microprocessors/stm32l4x2.html)
|
||||
* [STM32L422](https://www.st.com/en/microcontrollers-microprocessors/stm32l4x2.html)
|
||||
* [STM32L432](https://www.st.com/en/microcontrollers-microprocessors/stm32l4x2.html)
|
||||
|
@ -439,7 +439,7 @@ Use these to enable or disable building certain features. The more you have enab
|
||||
* `UNICODE_ENABLE`
|
||||
* Unicode
|
||||
* `BLUETOOTH_ENABLE`
|
||||
* Current options are BluefruitLE, RN42
|
||||
* Current options are bluefruit_le, rn42
|
||||
* `SPLIT_KEYBOARD`
|
||||
* Enables split keyboard support (dual MCU like the let's split and bakingpy's boards) and includes all necessary files located at quantum/split_common
|
||||
* `CUSTOM_MATRIX`
|
||||
|
@ -36,7 +36,7 @@ If you need help you can [open an issue](https://github.com/qmk/qmk_firmware/iss
|
||||
Never made an open source contribution before? Wondering how contributions work in QMK? Here's a quick rundown!
|
||||
|
||||
0. Sign up for a [GitHub](https://github.com) account.
|
||||
1. Put together a keymap to contribute, [find an issue](https://github.com/qmk/qmk_firmware/issues) you are interested in addressing, or [a feature](https://github.com/qmk/qmk_firmware/issues?q=is%3Aopen+is%3Aissue+label%3Afeature) you would like to add.
|
||||
1. [Find an issue](https://github.com/qmk/qmk_firmware/issues) you are interested in addressing, or [a feature](https://github.com/qmk/qmk_firmware/issues?q=is%3Aopen+is%3Aissue+label%3Afeature) you would like to add.
|
||||
2. Fork the repository associated with the issue to your GitHub account. This means that you will have a copy of the repository under `your-GitHub-username/qmk_firmware`.
|
||||
3. Clone the repository to your local machine using `git clone https://github.com/github-username/repository-name.git`.
|
||||
4. If you're working on a new feature consider opening an issue to talk with us about the work you're about to undertake.
|
||||
@ -63,10 +63,11 @@ Most of our style is pretty easy to pick up on. If you are familiar with either
|
||||
|
||||
We have a few different types of changes in QMK, each requiring a different level of rigor. We'd like you to keep the following guidelines in mind no matter what type of change you're making.
|
||||
|
||||
* **Before you contribute:** Please make sure your fork is up to date with the upstream `qmk_firmware` repo. This will help minimize CI failures that may not occur for you when compiling locally.
|
||||
* Separate PRs into logical units. For example, do not submit one PR covering two separate features, instead submit a separate PR for each feature.
|
||||
* Check for unnecessary whitespace with `git diff --check` before committing.
|
||||
* Make sure your code change actually compiles.
|
||||
* Keymaps: Make sure that `make keyboard:your_new_keymap` does not return any errors.
|
||||
* Keymaps: Make sure that `make keyboard:keymap` does not return any errors.
|
||||
* Keyboards: Make sure that `make keyboard:all` does not return any errors.
|
||||
* Core: Make sure that `make all` does not return any errors.
|
||||
* Make sure commit messages are understandable on their own. You should put a short description (no more than 70 characters) on the first line, the second line should be empty, and on the 3rd and later lines you should describe your commit in detail, if required. Example:
|
||||
@ -113,16 +114,6 @@ or if you only have Python 3 installed:
|
||||
|
||||
and navigating to `http://localhost:8936/`.
|
||||
|
||||
## Keymaps
|
||||
|
||||
Most first-time QMK contributors start with their personal keymaps. We try to keep keymap standards pretty casual (keymaps, after all, reflect the personality of their creators) but we do ask that you follow these guidelines to make it easier for others to discover and learn from your keymap.
|
||||
|
||||
* Write a `readme.md` using [the template](documentation_templates.md).
|
||||
* All Keymap PRs are squashed, so if you care about how your commits are squashed you should do it yourself
|
||||
* Do not lump features in with keymap PRs. Submit the feature first and then a second PR for the keymap.
|
||||
* Do not include `Makefile`s in your keymap folder (they're no longer used)
|
||||
* Update copyrights in file headers (look for `%YOUR_NAME%`)
|
||||
|
||||
## Keyboards
|
||||
|
||||
Keyboards are the raison d'être for QMK. Some keyboards are community maintained, while others are maintained by the people responsible for making a particular keyboard. The `readme.md` should tell you who maintains a particular keyboard. If you have questions relating to a particular keyboard you can [Open An Issue](https://github.com/qmk/qmk_firmware/issues) and tag the maintainer in your question.
|
||||
@ -130,7 +121,7 @@ Keyboards are the raison d'être for QMK. Some keyboards are community maintaine
|
||||
We also ask that you follow these guidelines:
|
||||
|
||||
* Write a `readme.md` using [the template](documentation_templates.md).
|
||||
* Keep the number of commits reasonable or we will squash your PR
|
||||
* Include a `default` keymap that provides a clean slate for users to start with when creating their own keymaps.
|
||||
* Do not lump core features in with new keyboards. Submit the feature first and then submit a separate PR for the keyboard.
|
||||
* Name `.c`/`.h` file after the immediate parent folder, eg `/keyboards/<kb1>/<kb2>/<kb2>.[ch]`
|
||||
* Do not include `Makefile`s in your keyboard folder (they're no longer used)
|
||||
|
@ -79,8 +79,8 @@ If you are not sure how to edit this file or are not comfortable with Python [op
|
||||
|
||||
The final piece of the puzzle is providing your new option to the build system. This is done by generating two files:
|
||||
|
||||
* `.build/obj_<keyboard>/src/info_config.h`
|
||||
* `.build/obj_<keyboard>/src/rules.mk`
|
||||
* `.build/obj_<keyboard>_<keymap>/src/info_config.h`
|
||||
* `.build/obj_<keyboard>_<keymap>/src/rules.mk`
|
||||
|
||||
These two files are generated by the code here:
|
||||
|
||||
|
@ -133,7 +133,17 @@ groups in the below fallback switch.
|
||||
### NO_AUTO_SHIFT_SPECIAL (simple define)
|
||||
|
||||
Do not Auto Shift special keys, which include -\_, =+, [{, ]}, ;:, '", ,<, .>,
|
||||
and /?
|
||||
/?, and the KC_TAB.
|
||||
|
||||
### NO_AUTO_SHIFT_TAB (simple define)
|
||||
|
||||
Do not Auto Shift KC_TAB but leave Auto Shift enabled for the other special
|
||||
characters.
|
||||
|
||||
### NO_AUTO_SHIFT_SYMBOLS (simple define)
|
||||
|
||||
Do not Auto Shift symbol keys, which include -\_, =+, [{, ]}, ;:, '", ,<, .>,
|
||||
and /?.
|
||||
|
||||
### NO_AUTO_SHIFT_NUMERIC (simple define)
|
||||
|
||||
@ -143,9 +153,13 @@ Do not Auto Shift numeric keys, zero through nine.
|
||||
|
||||
Do not Auto Shift alpha characters, which include A through Z.
|
||||
|
||||
### AUTO_SHIFT_ENTER (simple define)
|
||||
|
||||
Auto Shift the enter key.
|
||||
|
||||
### Auto Shift Per Key
|
||||
|
||||
There are functions that allows you to determine which keys shold be autoshifted, much like the tap-hold keys.
|
||||
There are functions that allows you to determine which keys should be autoshifted, much like the tap-hold keys.
|
||||
|
||||
The first of these, used to simply add a key to Auto Shift, is `get_custom_auto_shifted_key`:
|
||||
|
||||
@ -172,9 +186,15 @@ bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
|
||||
case KC_1 ... KC_0:
|
||||
# endif
|
||||
# ifndef NO_AUTO_SHIFT_SPECIAL
|
||||
# ifndef NO_AUTO_SHIFT_TAB
|
||||
case KC_TAB:
|
||||
case KC_MINUS ... KC_SLASH:
|
||||
case KC_NONUS_BACKSLASH:
|
||||
# endif
|
||||
# ifndef NO_AUTO_SHIFT_SYMBOLS
|
||||
case AUTO_SHIFT_SYMBOLS:
|
||||
# endif
|
||||
# endif
|
||||
# ifdef AUTO_SHIFT_ENTER
|
||||
case KC_ENT:
|
||||
# endif
|
||||
return true;
|
||||
}
|
||||
@ -192,6 +212,25 @@ Enables keyrepeat.
|
||||
|
||||
Disables automatically keyrepeating when `AUTO_SHIFT_TIMEOUT` is exceeded.
|
||||
|
||||
|
||||
### AUTO_SHIFT_ALPHA (predefined key group)
|
||||
|
||||
A predefined group of keys representing A through Z.
|
||||
|
||||
### AUTO_SHIFT_NUMERIC (predefined key group)
|
||||
|
||||
A predefined group of keys representing 0 through 9. Note, these are defined as
|
||||
1 through 0 since that is the order they normally appear in.
|
||||
|
||||
### AUTO_SHIFT_SYMBOLS (predefined key group)
|
||||
|
||||
A predefined group of keys representing symbolic characters which include -\_, =+, [{, ]}, ;:, '", ,<, .>,
|
||||
and /?.
|
||||
|
||||
### AUTO_SHIFT_SPECIAL (predefined key group)
|
||||
|
||||
A predefined group of keys that combines AUTO_SHIFT_SYMBOLS and KC_TAB.
|
||||
|
||||
## Custom Shifted Values
|
||||
|
||||
Especially on small keyboards, the default shifted value for many keys is not
|
||||
|
@ -198,7 +198,9 @@ bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *t
|
||||
|
||||
### Apply Autocorrect
|
||||
|
||||
Additionally, `apply_autocorrect(uint8_t backspaces, const char *str)` allows for users to add additional handling to the autocorrection, or replace the functionality entirely. This passes on the number of backspaces needed to replace the words, as well as the replacement string (partial word, not the full word).
|
||||
Additionally, `apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct)` allows for users to add additional handling to the autocorrection, or replace the functionality entirely. This passes on the number of backspaces needed to replace the words, as well as the replacement string (partial word, not the full word), and the typo and corrected strings (complete words).
|
||||
|
||||
?> Due to the way code works (no notion of words, just a stream of letters), the `typo` and `correct` strings are a best bet and could be "wrong". For example you may get `wordtpyo` & `wordtypo` instead of the expected `tpyo` & `typo`.
|
||||
|
||||
#### Apply Autocorrect Example
|
||||
|
||||
@ -209,7 +211,7 @@ This following example will play a sound when a typo is autocorrected and execut
|
||||
float autocorrect_song[][2] = SONG(TERMINAL_SOUND);
|
||||
#endif
|
||||
|
||||
bool apply_autocorrect(uint8_t backspaces, const char *str) {
|
||||
bool apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct) {
|
||||
#ifdef AUDIO_ENABLE
|
||||
PLAY_SONG(autocorrect_song);
|
||||
#endif
|
||||
@ -223,14 +225,17 @@ bool apply_autocorrect(uint8_t backspaces, const char *str) {
|
||||
|
||||
?> In this callback function, `return false` will stop the normal processing of autocorrect, which requires manually handling of removing the "bad" characters and typing the new characters.
|
||||
|
||||
!> ***IMPORTANT***: `str` is a pointer to `PROGMEM` data for the autocorrection. If you return false, and want to send the string, this needs to use `send_string_P` and not `send_string` or `SEND_STRING`.
|
||||
!> ***IMPORTANT***: `str` is a pointer to `PROGMEM` data for the autocorrection. If you return false, and want to send the string, this needs to use `send_string_P` and not `send_string` nor `SEND_STRING`.
|
||||
|
||||
You can also use `apply_autocorrect` to detect and display the event but allow internal code to execute the autocorrection with `return true`:
|
||||
|
||||
```c
|
||||
bool apply_autocorrect(uint8_t backspaces, const char *str) {
|
||||
bool apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct) {
|
||||
#ifdef OLED_ENABLE
|
||||
oled_write_P(PSTR("Auto-corrected"), false);
|
||||
#endif
|
||||
#ifdef CONSOLE_ENABLE
|
||||
printf("'%s' was corrected to '%s'\n", typo, correct);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
# Backlighting :id=backlighting
|
||||
|
||||
Many keyboards support backlit keys by way of individual LEDs placed through or underneath the keyswitches. This feature is distinct from both the [RGB underglow](feature_rgblight.md) and [RGB matrix](feature_rgb_matrix.md) features as it usually allows for only a single colour per switch, though you can obviously install multiple different single coloured LEDs on a keyboard.
|
||||
Many keyboards support backlit keys by way of individual LEDs placed through or underneath the keyswitches. This feature is distinct from both the [RGB Underglow](feature_rgblight.md) and [RGB Matrix](feature_rgb_matrix.md) features as it usually allows for only a single colour per switch, though you can obviously install multiple different single coloured LEDs on a keyboard.
|
||||
|
||||
QMK is able to control the brightness of these LEDs by switching them on and off rapidly in a certain ratio, a technique known as *Pulse Width Modulation*, or PWM. By altering the duty cycle of the PWM signal, it creates the illusion of dimming.
|
||||
|
||||
The MCU can only supply so much current to its GPIO pins. Instead of powering the backlight directly from the MCU, the backlight pin is connected to a transistor or MOSFET that switches the power to the LEDs.
|
||||
## Usage :id=usage
|
||||
|
||||
Most keyboards have backlighting enabled by default if they support it, but if it is not working for you, check that your `rules.mk` includes the following:
|
||||
Most keyboards have backlighting enabled by default if they support it, but if it is not working for you (or you have added support), check that your `rules.mk` includes the following:
|
||||
|
||||
```make
|
||||
BACKLIGHT_ENABLE = yes
|
||||
@ -14,57 +14,23 @@ BACKLIGHT_ENABLE = yes
|
||||
|
||||
## Keycodes :id=keycodes
|
||||
|
||||
Once enabled, the following keycodes below can be used to change the backlight level.
|
||||
|Key |Aliases |Description |
|
||||
|-------------------------------|---------|-----------------------------------|
|
||||
|`QK_BACKLIGHT_TOGGLE` |`BL_TOGG`|Turn the backlight on or off |
|
||||
|`QK_BACKLIGHT_STEP` |`BL_STEP`|Cycle through backlight levels |
|
||||
|`QK_BACKLIGHT_ON` |`BL_ON` |Set the backlight to max brightness|
|
||||
|`QK_BACKLIGHT_OFF` |`BL_OFF` |Turn the backlight off |
|
||||
|`QK_BACKLIGHT_UP` |`BL_UP` |Increase the backlight level |
|
||||
|`QK_BACKLIGHT_DOWN` |`BL_DOWN`|Decrease the backlight level |
|
||||
|`QK_BACKLIGHT_TOGGLE_BREATHING`|`BL_BRTG`|Toggle backlight breathing |
|
||||
|
||||
| Key | Aliases | Description |
|
||||
|---------------------------------|-----------|-------------------------------------|
|
||||
| `QK_BACKLIGHT_TOGGLE` | `BL_TOGG` | Turn the backlight on or off |
|
||||
| `QK_BACKLIGHT_STEP` | `BL_STEP` | Cycle through backlight levels |
|
||||
| `QK_BACKLIGHT_ON` | `BL_ON` | Set the backlight to max brightness |
|
||||
| `QK_BACKLIGHT_OFF` | `BL_OFF` | Turn the backlight off |
|
||||
| `QK_BACKLIGHT_UP` | `BL_UP` | Increase the backlight level |
|
||||
| `QK_BACKLIGHT_DOWN` | `BL_DOWN` | Decrease the backlight level |
|
||||
| `QK_BACKLIGHT_TOGGLE_BREATHING` | `BL_BRTG` | Toggle backlight breathing |
|
||||
## Basic Configuration :id=basic-configuration
|
||||
|
||||
## Functions :id=functions
|
||||
|
||||
These functions can be used to change the backlighting in custom code:
|
||||
|
||||
|Function |Description |
|
||||
|------------------------|--------------------------------------------|
|
||||
|`backlight_toggle()` |Turn the backlight on or off |
|
||||
|`backlight_enable()` |Turn the backlight on |
|
||||
|`backlight_disable()` |Turn the backlight off |
|
||||
|`backlight_step()` |Cycle through backlight levels |
|
||||
|`backlight_increase()` |Increase the backlight level |
|
||||
|`backlight_decrease()` |Decrease the backlight level |
|
||||
|`backlight_level(x)` |Sets the backlight level to specified level |
|
||||
|`get_backlight_level()` |Return the current backlight level |
|
||||
|`is_backlight_enabled()`|Return whether the backlight is currently on|
|
||||
|
||||
If backlight breathing is enabled (see below), the following functions are also available:
|
||||
|
||||
|Function |Description |
|
||||
|---------------------|--------------------------------------|
|
||||
|`breathing_toggle()` |Turn the backlight breathing on or off|
|
||||
|`breathing_enable()` |Turns on backlight breathing |
|
||||
|`breathing_disable()`|Turns off backlight breathing |
|
||||
|
||||
## Configuration :id=configuration
|
||||
|
||||
To select which driver to use, configure your `rules.mk` with the following:
|
||||
|
||||
```make
|
||||
BACKLIGHT_DRIVER = software
|
||||
```
|
||||
|
||||
Valid driver values are `pwm`, `software`, `custom` or `no`. See below for help on individual drivers.
|
||||
|
||||
To configure the backlighting, `#define` these in your `config.h`:
|
||||
Add the following to your `config.h`:
|
||||
|
||||
|Define |Default |Description |
|
||||
|-----------------------------|------------------|-----------------------------------------------------------------------------------------------------------------|
|
||||
|`BACKLIGHT_PIN` |*Not defined* |The pin that controls the LED(s) |
|
||||
|`BACKLIGHT_PIN` |*Not defined* |The pin that controls the LEDs |
|
||||
|`BACKLIGHT_LEVELS` |`3` |The number of brightness levels (maximum 31 excluding off) |
|
||||
|`BACKLIGHT_CAPS_LOCK` |*Not defined* |Enable Caps Lock indicator using backlight (for keyboards without dedicated LED) |
|
||||
|`BACKLIGHT_BREATHING` |*Not defined* |Enable backlight breathing, if supported |
|
||||
@ -76,124 +42,66 @@ To configure the backlighting, `#define` these in your `config.h`:
|
||||
|
||||
Unless you are designing your own keyboard, you generally should not need to change the `BACKLIGHT_PIN` or `BACKLIGHT_ON_STATE`.
|
||||
|
||||
### Backlight On State :id=backlight-on-state
|
||||
### "On" State :id=on-state
|
||||
|
||||
Most backlight circuits are driven by an N-channel MOSFET or NPN transistor. This means that to turn the transistor *on* and light the LEDs, you must drive the backlight pin, connected to the gate or base, *high*.
|
||||
Sometimes, however, a P-channel MOSFET, or a PNP transistor is used. In this case, when the transistor is on, the pin is driven *low* instead.
|
||||
|
||||
This functionality is configured at the keyboard level with the `BACKLIGHT_ON_STATE` define.
|
||||
To configure the "on" state of the backlight circuit, add the following to your `config.h`:
|
||||
|
||||
### AVR Driver :id=avr-driver
|
||||
|
||||
The `pwm` driver is configured by default, however the equivalent setting within `rules.mk` would be:
|
||||
|
||||
```make
|
||||
BACKLIGHT_DRIVER = pwm
|
||||
```c
|
||||
#define BACKLIGHT_ON_STATE 0
|
||||
```
|
||||
|
||||
#### Caveats :id=avr-caveats
|
||||
|
||||
On AVR boards, QMK automatically decides which driver to use according to the following table:
|
||||
|
||||
|Backlight Pin|AT90USB64/128|AT90USB162|ATmega16/32U4|ATmega16/32U2|ATmega32A|ATmega328/P|
|
||||
|-------------|-------------|----------|-------------|-------------|---------|-----------|
|
||||
|`B1` | | | | | |Timer 1 |
|
||||
|`B2` | | | | | |Timer 1 |
|
||||
|`B5` |Timer 1 | |Timer 1 | | | |
|
||||
|`B6` |Timer 1 | |Timer 1 | | | |
|
||||
|`B7` |Timer 1 |Timer 1 |Timer 1 |Timer 1 | | |
|
||||
|`C4` |Timer 3 | | | | | |
|
||||
|`C5` |Timer 3 |Timer 1 | |Timer 1 | | |
|
||||
|`C6` |Timer 3 |Timer 1 |Timer 3 |Timer 1 | | |
|
||||
|`D4` | | | | |Timer 1 | |
|
||||
|`D5` | | | | |Timer 1 | |
|
||||
|
||||
All other pins will use timer-assisted software PWM:
|
||||
|
||||
|Audio Pin|Audio Timer|Software PWM Timer|
|
||||
|---------|-----------|------------------|
|
||||
|`C4` |Timer 3 |Timer 1 |
|
||||
|`C5` |Timer 3 |Timer 1 |
|
||||
|`C6` |Timer 3 |Timer 1 |
|
||||
|`B5` |Timer 1 |Timer 3 |
|
||||
|`B6` |Timer 1 |Timer 3 |
|
||||
|`B7` |Timer 1 |Timer 3 |
|
||||
|
||||
When both timers are in use for Audio, the backlight PWM cannot use a hardware timer, and will instead be triggered during the matrix scan. In this case, breathing is not supported, and the backlight might flicker, because the PWM computation may not be called with enough timing precision.
|
||||
|
||||
#### Hardware PWM Implementation :id=hardware-pwm-implementation
|
||||
|
||||
When using the supported pins for backlighting, QMK will use a hardware timer configured to output a PWM signal. This timer will count up to `ICRx` (by default `0xFFFF`) before resetting to 0.
|
||||
The desired brightness is calculated and stored in the `OCRxx` register. When the counter reaches this value, the backlight pin will go low, and is pulled high again when the counter resets.
|
||||
In this way `OCRxx` essentially controls the duty cycle of the LEDs, and thus the brightness, where `0x0000` is completely off and `0xFFFF` is completely on.
|
||||
|
||||
The breathing effect is achieved by registering an interrupt handler for `TIMER1_OVF_vect` that is called whenever the counter resets, roughly 244 times per second.
|
||||
In this handler, the value of an incrementing counter is mapped onto a precomputed brightness curve. To turn off breathing, the interrupt handler is simply disabled, and the brightness reset to the level stored in EEPROM.
|
||||
|
||||
#### Timer Assisted PWM Implementation :id=timer-assisted-implementation
|
||||
|
||||
When `BACKLIGHT_PIN` is not set to a hardware backlight pin, QMK will use a hardware timer configured to trigger software interrupts. This time will count up to `ICRx` (by default `0xFFFF`) before resetting to 0.
|
||||
When resetting to 0, the CPU will fire an OVF (overflow) interrupt that will turn the LEDs on, starting the duty cycle.
|
||||
The desired brightness is calculated and stored in the `OCRxx` register. When the counter reaches this value, the CPU will fire a Compare Output match interrupt, which will turn the LEDs off.
|
||||
In this way `OCRxx` essentially controls the duty cycle of the LEDs, and thus the brightness, where `0x0000` is completely off and `0xFFFF` is completely on.
|
||||
|
||||
The breathing effect is the same as in the hardware PWM implementation.
|
||||
|
||||
### ARM Driver :id=arm-configuration
|
||||
|
||||
While still in its early stages, ARM backlight support aims to eventually have feature parity with AVR. The `pwm` driver is configured by default, however the equivalent setting within `rules.mk` would be:
|
||||
|
||||
```make
|
||||
BACKLIGHT_DRIVER = pwm
|
||||
```
|
||||
|
||||
#### ChibiOS Configuration :id=arm-configuration
|
||||
|
||||
The following `#define`s apply only to ARM-based keyboards:
|
||||
|
||||
|Define |Default|Description |
|
||||
|-----------------------|-------|-----------------------------------|
|
||||
|`BACKLIGHT_PWM_DRIVER` |`PWMD4`|The PWM driver to use |
|
||||
|`BACKLIGHT_PWM_CHANNEL`|`3` |The PWM channel to use |
|
||||
|`BACKLIGHT_PAL_MODE` |`2` |The pin alternative function to use|
|
||||
|
||||
See the ST datasheet for your particular MCU to determine these values. Unless you are designing your own keyboard, you generally should not need to change them.
|
||||
|
||||
#### Caveats :id=arm-caveats
|
||||
|
||||
Currently only hardware PWM is supported, not timer assisted, and does not provide automatic configuration.
|
||||
|
||||
### Software PWM Driver :id=software-pwm-driver
|
||||
|
||||
In this mode, PWM is "emulated" while running other keyboard tasks. It offers maximum hardware compatibility without extra platform configuration. The tradeoff is the backlight might jitter when the keyboard is busy. To enable, add this to your `rules.mk`:
|
||||
|
||||
```make
|
||||
BACKLIGHT_DRIVER = software
|
||||
```
|
||||
|
||||
#### Multiple Backlight Pins :id=multiple-backlight-pins
|
||||
### Multiple Backlight Pins :id=multiple-backlight-pins
|
||||
|
||||
Most keyboards have only one backlight pin which controls all backlight LEDs (especially if the backlight is connected to a hardware PWM pin).
|
||||
In software PWM, it is possible to define multiple backlight pins, which will be turned on and off at the same time during the PWM duty cycle.
|
||||
The `timer` and `software` drivers allow you to define multiple backlight pins, which will be turned on and off at the same time during the PWM duty cycle.
|
||||
|
||||
This feature allows to set, for instance, the Caps Lock LED's (or any other controllable LED) brightness at the same level as the other LEDs of the backlight. This is useful if you have mapped Control in place of Caps Lock and you need the Caps Lock LED to be part of the backlight instead of being activated when Caps Lock is on, as it is usually wired to a separate pin from the backlight.
|
||||
|
||||
To activate multiple backlight pins, add something like this to your `config.h`, instead of `BACKLIGHT_PIN`:
|
||||
To configure multiple backlight pins, add something like this to your `config.h`, instead of `BACKLIGHT_PIN`:
|
||||
|
||||
```c
|
||||
#define BACKLIGHT_PINS { F5, B2 }
|
||||
```
|
||||
|
||||
## Driver Configuration :id=driver-configuration
|
||||
|
||||
Backlight driver selection is configured in `rules.mk`. Valid drivers are `pwm` (default), `timer`, `software`, or `custom`. See below for information on individual drivers.
|
||||
|
||||
### PWM Driver :id=pwm-driver
|
||||
|
||||
This is the default backlight driver, which leverages the hardware PWM output capability of the microcontroller.
|
||||
|
||||
```make
|
||||
BACKLIGHT_DRIVER = pwm
|
||||
```
|
||||
|
||||
### Timer Driver :id=timer-driver
|
||||
|
||||
This driver is similar to the PWM driver, but instead of directly configuring the pin to output a PWM signal, an interrupt handler is attached to the timer to turn the pin on and off as appropriate.
|
||||
|
||||
```make
|
||||
BACKLIGHT_DRIVER = timer
|
||||
```
|
||||
|
||||
### Software Driver :id=software-driver
|
||||
|
||||
In this mode, PWM is "emulated" while running other keyboard tasks. It offers maximum hardware compatibility without extra platform configuration. However, breathing is not supported, and the backlight can flicker when the keyboard is busy.
|
||||
|
||||
```make
|
||||
BACKLIGHT_DRIVER = software
|
||||
```
|
||||
|
||||
### Custom Driver :id=custom-driver
|
||||
|
||||
If none of the above drivers apply to your board (for example, you are using a separate IC to control the backlight), you can implement a custom backlight driver using this simple API provided by QMK. To enable, add this to your `rules.mk`:
|
||||
If none of the above drivers apply to your board (for example, you are using a separate IC to control the backlight), you can implement a custom backlight driver using a simple API.
|
||||
|
||||
```make
|
||||
BACKLIGHT_DRIVER = custom
|
||||
```
|
||||
|
||||
Then implement any of these hooks:
|
||||
|
||||
```c
|
||||
void backlight_init_ports(void) {
|
||||
// Optional - runs on startup
|
||||
@ -211,10 +119,188 @@ void backlight_task(void) {
|
||||
}
|
||||
```
|
||||
|
||||
## AVR Configuration :id=avr-configuration
|
||||
|
||||
### PWM Driver :id=avr-pwm-driver
|
||||
|
||||
The following table describes the supported pins for the PWM driver. Only cells marked with a timer number are capable of hardware PWM output; any others must use the `timer` driver.
|
||||
|
||||
|Backlight Pin|AT90USB64/128|AT90USB162|ATmega16/32U4|ATmega16/32U2|ATmega32A|ATmega328/P|
|
||||
|-------------|-------------|----------|-------------|-------------|---------|-----------|
|
||||
|`B1` | | | | | |Timer 1 |
|
||||
|`B2` | | | | | |Timer 1 |
|
||||
|`B5` |Timer 1 | |Timer 1 | | | |
|
||||
|`B6` |Timer 1 | |Timer 1 | | | |
|
||||
|`B7` |Timer 1 |Timer 1 |Timer 1 |Timer 1 | | |
|
||||
|`C4` |Timer 3 | | | | | |
|
||||
|`C5` |Timer 3 |Timer 1 | |Timer 1 | | |
|
||||
|`C6` |Timer 3 |Timer 1 |Timer 3 |Timer 1 | | |
|
||||
|`D4` | | | | |Timer 1 | |
|
||||
|`D5` | | | | |Timer 1 | |
|
||||
|
||||
### Timer Driver :id=avr-timer-driver
|
||||
|
||||
Any GPIO pin can be used with this driver. The following table describes the supported timers:
|
||||
|
||||
|AT90USB64/128|AT90USB162|ATmega16/32U4|ATmega16/32U2|ATmega32A|ATmega328/P|
|
||||
|-------------|----------|-------------|-------------|---------|-----------|
|
||||
|Timers 1 & 3 |Timer 1 |Timers 1 & 3 |Timer 1 |Timer 1 |Timer 1 |
|
||||
|
||||
The following `#define`s apply only to the `timer` driver:
|
||||
|
||||
|Define |Default|Description |
|
||||
|-----------------------|-------|----------------|
|
||||
|`BACKLIGHT_PWM_TIMER` |`1` |The timer to use|
|
||||
|
||||
Note that the choice of timer may conflict with the [Audio](feature_audio.md) feature.
|
||||
|
||||
## ChibiOS/ARM Configuration :id=arm-configuration
|
||||
|
||||
### PWM Driver :id=arm-pwm-driver
|
||||
|
||||
Depending on the ChibiOS board configuration, you may need to enable PWM at the keyboard level. For STM32, this would look like:
|
||||
|
||||
`halconf.h`:
|
||||
```c
|
||||
#define HAL_USE_PWM TRUE
|
||||
```
|
||||
`mcuconf.h`:
|
||||
```c
|
||||
#undef STM32_PWM_USE_TIM4
|
||||
#define STM32_PWM_USE_TIM4 TRUE
|
||||
```
|
||||
|
||||
The following `#define`s apply only to the `pwm` driver:
|
||||
|
||||
|Define |Default |Description |
|
||||
|-----------------------|--------|-----------------------------------|
|
||||
|`BACKLIGHT_PWM_DRIVER` |`PWMD4` |The PWM driver to use |
|
||||
|`BACKLIGHT_PWM_CHANNEL`|`3` |The PWM channel to use |
|
||||
|`BACKLIGHT_PAL_MODE` |`2` |The pin alternative function to use|
|
||||
|
||||
Refer to the ST datasheet for your particular MCU to determine these values. For example, these defaults are set up for pin `B8` on a Proton-C (STM32F303) using `TIM4_CH3` on AF2. Unless you are designing your own keyboard, you generally should not need to change them.
|
||||
|
||||
### Timer Driver :id=arm-timer-driver
|
||||
|
||||
Depending on the ChibiOS board configuration, you may need to enable general-purpose timers at the keyboard level. For STM32, this would look like:
|
||||
|
||||
`halconf.h`:
|
||||
```c
|
||||
#define HAL_USE_GPT TRUE
|
||||
```
|
||||
`mcuconf.h`:
|
||||
```c
|
||||
#undef STM32_GPT_USE_TIM15
|
||||
#define STM32_GPT_USE_TIM15 TRUE
|
||||
```
|
||||
|
||||
The following `#define`s apply only to the `timer` driver:
|
||||
|
||||
|Define |Default |Description |
|
||||
|----------------------|--------|----------------|
|
||||
|`BACKLIGHT_GPT_DRIVER`|`GPTD15`|The timer to use|
|
||||
|
||||
## Example Schematic
|
||||
|
||||
Since the MCU can only supply so much current to its GPIO pins, instead of powering the backlight directly from the MCU, the backlight pin is connected to a transistor or MOSFET that switches the power to the LEDs.
|
||||
|
||||
In this typical example, the backlight LEDs are all connected in parallel towards an N-channel MOSFET. Its gate pin is wired to one of the microcontroller's GPIO pins through a 470Ω resistor to avoid ringing.
|
||||
A pulldown resistor is also placed between the gate pin and ground to keep it at a defined state when it is not otherwise being driven by the MCU.
|
||||
The values of these resistors are not critical - see [this Electronics StackExchange question](https://electronics.stackexchange.com/q/68748) for more information.
|
||||
|
||||
![Backlight example circuit](https://i.imgur.com/BmAvoUC.png)
|
||||
|
||||
## API :id=api
|
||||
|
||||
### `void backlight_toggle(void)` :id=api-backlight-toggle
|
||||
|
||||
Toggle the backlight on or off.
|
||||
|
||||
---
|
||||
|
||||
### `void backlight_enable(void)` :id=api-backlight-enable
|
||||
|
||||
Turn the backlight on.
|
||||
|
||||
---
|
||||
|
||||
### `void backlight_disable(void)` :id=api-backlight-disable
|
||||
|
||||
Turn the backlight off.
|
||||
|
||||
---
|
||||
|
||||
### `void backlight_step(void)` :id=api-backlight-step
|
||||
|
||||
Cycle through backlight levels.
|
||||
|
||||
---
|
||||
|
||||
### `void backlight_increase(void)` :id=api-backlight-increase
|
||||
|
||||
Increase the backlight level.
|
||||
|
||||
---
|
||||
|
||||
### `void backlight_decrease(void)` :id=api-backlight-decrease
|
||||
|
||||
Decrease the backlight level.
|
||||
|
||||
---
|
||||
|
||||
### `void backlight_level(uint8_t level)` :id=api-backlight-level
|
||||
|
||||
Set the backlight level.
|
||||
|
||||
#### Arguments :id=api-backlight-level-arguments
|
||||
|
||||
- `uint8_t level`
|
||||
The level to set, from 0 to `BACKLIGHT_LEVELS`.
|
||||
|
||||
---
|
||||
|
||||
### `uint8_t get_backlight_level(void)` :id=api-get-backlight-level
|
||||
|
||||
Get the current backlight level.
|
||||
|
||||
#### Return Value :id=api-get-backlight-level-return
|
||||
|
||||
The current backlight level, from 0 to `BACKLIGHT_LEVELS`.
|
||||
|
||||
---
|
||||
|
||||
### `bool is_backlight_enabled(void)` :id=api-is-backlight-enabled
|
||||
|
||||
Get the current backlight state.
|
||||
|
||||
#### Return Value :id=api-is-backlight-enabled-return
|
||||
|
||||
`true` if the backlight is enabled.
|
||||
|
||||
---
|
||||
|
||||
### `void backlight_toggle_breathing(void)` :id=api-backlight-toggle-breathing
|
||||
|
||||
Toggle backlight breathing on or off.
|
||||
|
||||
---
|
||||
|
||||
### `void backlight_enable_breathing(void)` :id=api-backlight-enable-breathing
|
||||
|
||||
Turn backlight breathing on.
|
||||
|
||||
---
|
||||
|
||||
### `void backlight_disable_breathing(void)` :id=api-backlight-disable-breathing
|
||||
|
||||
Turn backlight breathing off.
|
||||
|
||||
---
|
||||
|
||||
### `bool is_backlight_breathing(void)` :id=api-is-backlight-breathing
|
||||
|
||||
Get the current backlight breathing state.
|
||||
|
||||
#### Return Value :id=api-is-backlight-breathing-return
|
||||
|
||||
`true` if backlight breathing is enabled.
|
||||
|
@ -5,9 +5,9 @@
|
||||
Currently Bluetooth support is limited to AVR based chips. For Bluetooth 2.1, QMK has support for RN-42 modules. For more recent BLE protocols, currently only the Adafruit Bluefruit SPI Friend is directly supported. BLE is needed to connect to iOS devices. Note iOS does not support mouse input.
|
||||
|
||||
|Board |Bluetooth Protocol |Connection Type|rules.mk |Bluetooth Chip|
|
||||
|----------------------------------------------------------------|--------------------|---------------|--------------------------------|--------------|
|
||||
|Roving Networks RN-42 (Sparkfun Bluesmirf) |Bluetooth Classic |UART |`BLUETOOTH_DRIVER = RN42` |RN-42 |
|
||||
|[Bluefruit LE SPI Friend](https://www.adafruit.com/product/2633)|Bluetooth Low Energy|SPI |`BLUETOOTH_DRIVER = BluefruitLE`|nRF51822 |
|
||||
|----------------------------------------------------------------|--------------------|---------------|---------------------------------|--------------|
|
||||
|Roving Networks RN-42 (Sparkfun Bluesmirf) |Bluetooth Classic |UART |`BLUETOOTH_DRIVER = rn42` |RN-42 |
|
||||
|[Bluefruit LE SPI Friend](https://www.adafruit.com/product/2633)|Bluetooth Low Energy|SPI |`BLUETOOTH_DRIVER = bluefruit_le`|nRF51822 |
|
||||
|
||||
Not Supported Yet but possible:
|
||||
* [Bluefruit LE UART Friend](https://www.adafruit.com/product/2479). [Possible tmk implementation found in](https://github.com/tmk/tmk_keyboard/issues/514)
|
||||
@ -32,7 +32,7 @@ Add the following to your `rules.mk`:
|
||||
|
||||
```make
|
||||
BLUETOOTH_ENABLE = yes
|
||||
BLUETOOTH_DRIVER = BluefruitLE # or RN42
|
||||
BLUETOOTH_DRIVER = bluefruit_le # or rn42
|
||||
```
|
||||
|
||||
## Bluetooth Keycodes
|
||||
|
@ -4,11 +4,12 @@
|
||||
|
||||
The following options are currently available for haptic feedback in `rules.mk`:
|
||||
|
||||
```
|
||||
```make
|
||||
HAPTIC_ENABLE = yes
|
||||
|
||||
HAPTIC_DRIVER += DRV2605L
|
||||
HAPTIC_DRIVER += SOLENOID
|
||||
HAPTIC_DRIVER = drv2605l
|
||||
# or
|
||||
HAPTIC_DRIVER = solenoid
|
||||
```
|
||||
|
||||
The following `config.h` settings are available for all types of haptic feedback:
|
||||
@ -92,30 +93,30 @@ This driver supports 2 different feedback motors. Set the following in your `con
|
||||
|
||||
Eccentric Rotating Mass vibration motors (ERM) is motor with a off-set weight attached so when drive signal is attached, the off-set weight spins and causes a sinusoidal wave that translate into vibrations.
|
||||
|
||||
```
|
||||
#define FB_ERM_LRA 0
|
||||
#define FB_BRAKEFACTOR 3 /* For 1x:0, 2x:1, 3x:2, 4x:3, 6x:4, 8x:5, 16x:6, Disable Braking:7 */
|
||||
#define FB_LOOPGAIN 1 /* For Low:0, Medium:1, High:2, Very High:3 */
|
||||
```c
|
||||
#define DRV2605L_FB_ERM_LRA 0
|
||||
#define DRV2605L_FB_BRAKEFACTOR 3 /* For 1x:0, 2x:1, 3x:2, 4x:3, 6x:4, 8x:5, 16x:6, Disable Braking:7 */
|
||||
#define DRV2605L_FB_LOOPGAIN 1 /* For Low:0, Medium:1, High:2, Very High:3 */
|
||||
|
||||
/* Please refer to your datasheet for the optimal setting for your specific motor. */
|
||||
#define RATED_VOLTAGE 3
|
||||
#define V_PEAK 5
|
||||
#define DRV2605L_RATED_VOLTAGE 3
|
||||
#define DRV2605L_V_PEAK 5
|
||||
```
|
||||
##### LRA
|
||||
|
||||
Linear resonant actuators (LRA, also know as a linear vibrator) works different from a ERM. A LRA has a weight and magnet suspended by springs and a voice coil. When the drive signal is applied, the weight would be vibrate on a single axis (side to side or up and down). Since the weight is attached to a spring, there is a resonance effect at a specific frequency. This frequency is where the LRA will operate the most efficiently. Refer to the motor's datasheet for the recommanded range for this frequency.
|
||||
|
||||
```
|
||||
#define FB_ERM_LRA 1
|
||||
#define FB_BRAKEFACTOR 3 /* For 1x:0, 2x:1, 3x:2, 4x:3, 6x:4, 8x:5, 16x:6, Disable Braking:7 */
|
||||
#define FB_LOOPGAIN 1 /* For Low:0, Medium:1, High:2, Very High:3 */
|
||||
```c
|
||||
#define DRV2605L_FB_ERM_LRA 1
|
||||
#define DRV2605L_FB_BRAKEFACTOR 3 /* For 1x:0, 2x:1, 3x:2, 4x:3, 6x:4, 8x:5, 16x:6, Disable Braking:7 */
|
||||
#define DRV2605L_FB_LOOPGAIN 1 /* For Low:0, Medium:1, High:2, Very High:3 */
|
||||
|
||||
/* Please refer to your datasheet for the optimal setting for your specific motor. */
|
||||
#define RATED_VOLTAGE 2
|
||||
#define V_PEAK 2.8
|
||||
#define V_RMS 2.0
|
||||
#define V_PEAK 2.1
|
||||
#define F_LRA 205 /* resonance freq */
|
||||
#define DRV2605L_RATED_VOLTAGE 2
|
||||
#define DRV2605L_V_PEAK 2.8
|
||||
#define DRV2605L_V_RMS 2.0
|
||||
#define DRV2605L_V_PEAK 2.1
|
||||
#define DRV2605L_F_LRA 205 /* resonance freq */
|
||||
```
|
||||
|
||||
#### DRV2605L waveform library
|
||||
@ -170,13 +171,13 @@ List of waveform sequences from the datasheet:
|
||||
| 42 | lg_dblclick_med_80 | 84 | transition_rampup_med_smooth1 | | |
|
||||
### Optional DRV2605L defines
|
||||
|
||||
```
|
||||
#define DRV_GREETING *sequence name or number*
|
||||
```c
|
||||
#define DRV2605L_GREETING *sequence name or number*
|
||||
```
|
||||
If haptic feedback is enabled, the keyboard will vibrate to a specific sequence during startup. That can be selected using the following define:
|
||||
|
||||
```
|
||||
#define DRV_MODE_DEFAULT *sequence name or number*
|
||||
```c
|
||||
#define DRV2605L_DEFAULT_MODE *sequence name or number*
|
||||
```
|
||||
This will set what sequence `HF_RST` will set as the active mode. If not defined, mode will be set to 1 when `HF_RST` is pressed.
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# HD44780 LCD Driver
|
||||
# HD44780 LCD Driver :id=hd44780-lcd-driver
|
||||
|
||||
## Supported Hardware
|
||||
## Supported Hardware :id=supported-hardware
|
||||
|
||||
LCD modules using [HD44780U](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) IC or equivalent, communicating in 4-bit mode.
|
||||
|
||||
@ -11,7 +11,7 @@ LCD modules using [HD44780U](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf
|
||||
|
||||
To run these modules at 3.3V, an additional MAX660 voltage converter IC must be soldered on, along with two 10µF capacitors. See [this page](https://www.codrey.com/electronic-circuits/hack-your-16x2-lcd/) for more details.
|
||||
|
||||
## Usage
|
||||
## Usage :id=usage
|
||||
|
||||
Add the following to your `rules.mk`:
|
||||
|
||||
@ -19,7 +19,7 @@ Add the following to your `rules.mk`:
|
||||
HD44780_ENABLE = yes
|
||||
```
|
||||
|
||||
## Basic Configuration
|
||||
## Basic Configuration :id=basic-configuration
|
||||
|
||||
Add the following to your `config.h`:
|
||||
|
||||
@ -33,9 +33,9 @@ Add the following to your `config.h`:
|
||||
|`HD44780_DISPLAY_LINES`|`2` |The number of visible lines on the display |
|
||||
|`HD44780_WRAP_LINES` |*Not defined* |If defined, input characters will wrap to the next line |
|
||||
|
||||
## Examples
|
||||
## Examples :id=examples
|
||||
|
||||
### Hello World
|
||||
### Hello World :id=example-hello-world
|
||||
|
||||
Add the following to your `keymap.c`:
|
||||
|
||||
@ -46,7 +46,7 @@ void keyboard_post_init_user(void) {
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Character Definition
|
||||
### Custom Character Definition :id=example-custom-character
|
||||
|
||||
Up to eight custom characters can be defined. This data is stored in the Character Generator RAM (CGRAM), and is not persistent across power cycles.
|
||||
|
||||
@ -77,15 +77,15 @@ void keyboard_post_init_user(void) {
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
## API :id=api
|
||||
|
||||
### `void hd44780_init(bool cursor, bool blink)`
|
||||
### `void hd44780_init(bool cursor, bool blink)` :id=api-hd44780-init
|
||||
|
||||
Initialize the display.
|
||||
|
||||
This function should be called only once, before any of the other functions can be called.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-hd44780-init-arguments
|
||||
|
||||
- `bool cursor`
|
||||
Whether to show the cursor.
|
||||
@ -94,7 +94,7 @@ This function should be called only once, before any of the other functions can
|
||||
|
||||
---
|
||||
|
||||
### `void hd44780_clear(void)`
|
||||
### `void hd44780_clear(void)` :id=api-hd44780-clear
|
||||
|
||||
Clear the display.
|
||||
|
||||
@ -102,7 +102,7 @@ This function is called on init.
|
||||
|
||||
---
|
||||
|
||||
### `void hd44780_home(void)`
|
||||
### `void hd44780_home(void)` :id=api-hd44780-home
|
||||
|
||||
Move the cursor to the home position.
|
||||
|
||||
@ -110,13 +110,13 @@ This function is called on init.
|
||||
|
||||
---
|
||||
|
||||
### `void hd44780_on(bool cursor, bool blink)`
|
||||
### `void hd44780_on(bool cursor, bool blink)` :id=api-hd44780-on
|
||||
|
||||
Turn the display on, and/or set the cursor properties.
|
||||
|
||||
This function is called on init.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-hd44780-on-arguments
|
||||
|
||||
- `bool cursor`
|
||||
Whether to show the cursor.
|
||||
@ -125,17 +125,17 @@ This function is called on init.
|
||||
|
||||
---
|
||||
|
||||
### `void hd44780_off(void)`
|
||||
### `void hd44780_off(void)` :id=api-hd44780-off
|
||||
|
||||
Turn the display off.
|
||||
|
||||
---
|
||||
|
||||
### `void hd44780_set_cursor(uint8_t col, uint8_t line)`
|
||||
### `void hd44780_set_cursor(uint8_t col, uint8_t line)` :id=api-hd44780-set-cursor
|
||||
|
||||
Move the cursor to the specified position on the display.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-hd44780-set-cursor-arguments
|
||||
|
||||
- `uint8_t col`
|
||||
The column number to move to, from 0 to 15 on 16x2 displays.
|
||||
@ -144,48 +144,48 @@ Move the cursor to the specified position on the display.
|
||||
|
||||
---
|
||||
|
||||
### `void hd44780_putc(char c)`
|
||||
### `void hd44780_putc(char c)` :id=api-hd44780-putc
|
||||
|
||||
Print a character to the display. The newline character `\n` will move the cursor to the start of the next line.
|
||||
|
||||
The exact character shown may depend on the ROM code of your particular display - refer to the datasheet for the full character set.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-hd44780-putc-arguments
|
||||
|
||||
- `char c`
|
||||
The character to print.
|
||||
|
||||
---
|
||||
|
||||
### `void hd44780_puts(const char *s)`
|
||||
### `void hd44780_puts(const char *s)` :id=api-hd44780-puts
|
||||
|
||||
Print a string of characters to the display.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-hd44780-puts-arguments
|
||||
|
||||
- `const char *s`
|
||||
The string to print.
|
||||
|
||||
---
|
||||
|
||||
### `void hd44780_puts_P(const char *s)`
|
||||
### `void hd44780_puts_P(const char *s)` :id=api-hd44780-puts-p
|
||||
|
||||
Print a string of characters from PROGMEM to the display.
|
||||
|
||||
On ARM devices, this function is simply an alias of `hd44780_puts()`.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-hd44780-puts-p-arguments
|
||||
|
||||
- `const char *s`
|
||||
The PROGMEM string to print (ie. `PSTR("Hello")`).
|
||||
|
||||
---
|
||||
|
||||
### `void hd44780_define_char(uint8_t index, uint8_t *data)`
|
||||
### `void hd44780_define_char(uint8_t index, uint8_t *data)` :id=api-hd44780-define-char
|
||||
|
||||
Define a custom character.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-hd44780-define-char-arguments
|
||||
|
||||
- `uint8_t index`
|
||||
The index of the custom character to define, from 0 to 7.
|
||||
@ -194,13 +194,13 @@ Define a custom character.
|
||||
|
||||
---
|
||||
|
||||
### `void hd44780_define_char_P(uint8_t index, const uint8_t *data)`
|
||||
### `void hd44780_define_char_P(uint8_t index, const uint8_t *data)` :id=api-hd44780-define-char-p
|
||||
|
||||
Define a custom character from PROGMEM.
|
||||
|
||||
On ARM devices, this function is simply an alias of `hd44780_define_char()`.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-hd44780-define-char-p-arguments
|
||||
|
||||
- `uint8_t index`
|
||||
The index of the custom character to define, from 0 to 7.
|
||||
@ -209,21 +209,21 @@ On ARM devices, this function is simply an alias of `hd44780_define_char()`.
|
||||
|
||||
---
|
||||
|
||||
### `bool hd44780_busy(void)`
|
||||
### `bool hd44780_busy(void)` :id=api-hd44780-busy
|
||||
|
||||
Indicates whether the display is currently processing, and cannot accept instructions.
|
||||
|
||||
#### Return Value
|
||||
#### Return Value :id=api-hd44780-busy-arguments
|
||||
|
||||
`true` if the display is busy.
|
||||
|
||||
---
|
||||
|
||||
### `void hd44780_write(uint8_t data, bool isData)`
|
||||
### `void hd44780_write(uint8_t data, bool isData)` :id=api-hd44780-write
|
||||
|
||||
Write a byte to the display.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-hd44780-write-arguments
|
||||
|
||||
- `uint8_t data`
|
||||
The byte to send to the display.
|
||||
@ -232,67 +232,67 @@ Write a byte to the display.
|
||||
|
||||
---
|
||||
|
||||
### `uint8_t hd44780_read(bool isData)`
|
||||
### `uint8_t hd44780_read(bool isData)` :id=api-hd44780-read
|
||||
|
||||
Read a byte from the display.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-hd44780-read-arguments
|
||||
|
||||
- `bool isData`
|
||||
Whether to read the current cursor position, or the character at the cursor.
|
||||
|
||||
#### Return Value
|
||||
#### Return Value :id=api-hd44780-read-return
|
||||
|
||||
If `isData` is `true`, the returned byte will be the character at the current DDRAM address. Otherwise, it will be the current DDRAM address and the busy flag.
|
||||
|
||||
---
|
||||
|
||||
### `void hd44780_command(uint8_t command)`
|
||||
### `void hd44780_command(uint8_t command)` :id=api-hd44780-command
|
||||
|
||||
Send a command to the display. Refer to the datasheet and `hd44780.h` for the valid commands and defines.
|
||||
|
||||
This function waits for the display to clear the busy flag before sending the command.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-hd44780-command-arguments
|
||||
|
||||
- `uint8_t command`
|
||||
The command to send.
|
||||
|
||||
---
|
||||
|
||||
### `void hd44780_data(uint8_t data)`
|
||||
### `void hd44780_data(uint8_t data)` :id=api-hd44780-data
|
||||
|
||||
Send a byte of data to the display.
|
||||
|
||||
This function waits for the display to clear the busy flag before sending the data.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-hd44780-data-arguments
|
||||
|
||||
- `uint8_t data`
|
||||
The byte of data to send.
|
||||
|
||||
---
|
||||
|
||||
### `void hd44780_set_cgram_address(uint8_t address)`
|
||||
### `void hd44780_set_cgram_address(uint8_t address)` :id=api-hd44780-set-cgram-address
|
||||
|
||||
Set the CGRAM address.
|
||||
|
||||
This function is used when defining custom characters.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-hd44780-set-cgram-address-arguments
|
||||
|
||||
- `uint8_t address`
|
||||
The CGRAM address to move to, from `0x00` to `0x3F`.
|
||||
|
||||
---
|
||||
|
||||
### `void hd44780_set_ddram_address(uint8_t address)`
|
||||
### `void hd44780_set_ddram_address(uint8_t address)` :id=api-hd44780-set-ddram-address
|
||||
|
||||
Set the DDRAM address.
|
||||
|
||||
This function is used when printing characters to the display, and setting the cursor.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-hd44780-set-ddram-address-arguments
|
||||
|
||||
- `uint8_t address`
|
||||
The DDRAM address to move to, from `0x00` to `0x7F`.
|
||||
|
@ -225,3 +225,30 @@ The duration of the key repeat delay is controlled with the `KEY_OVERRIDE_REPEAT
|
||||
## 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.
|
||||
|
||||
## Solution to the problem of flashing modifiers :id=neutralize-flashing-modifiers
|
||||
|
||||
If the programs you use bind an action to taps of modifier keys (e.g. tapping left GUI to bring up the applications menu or tapping left Alt to focus the menu bar), you may find that using key overrides with suppressed mods falsely triggers those actions. To counteract this, you can define a `DUMMY_MOD_NEUTRALIZER_KEYCODE` in `config.h` that will get sent in between the register and unregister events of a suppressed modifier. That way, the programs on your computer will no longer interpret the mod suppression induced by key overrides as a lone tap of a modifier key and will thus not falsely trigger the undesired action.
|
||||
|
||||
Naturally, for this technique to be effective, you must choose a `DUMMY_MOD_NEUTRALIZER_KEYCODE` for which no keyboard shortcuts are bound to. Recommended values are: `KC_RIGHT_CTRL` or `KC_F18`.
|
||||
Please note that `DUMMY_MOD_NEUTRALIZER_KEYCODE` must be a basic, unmodified, HID keycode so values like `KC_NO`, `KC_TRANSPARENT` or `KC_PIPE` aka `S(KC_BACKSLASH)` are not permitted.
|
||||
|
||||
By default, only left Alt and left GUI are neutralized. If you want to change the list of applicable modifier masks, use the following in your `config.h`:
|
||||
|
||||
```c
|
||||
#define MODS_TO_NEUTRALIZE { <mod_mask_1>, <mod_mask_2>, ... }
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
```c
|
||||
#define DUMMY_MOD_NEUTRALIZER_KEYCODE KC_RIGHT_CTRL
|
||||
|
||||
// Neutralize left alt and left GUI (Default value)
|
||||
#define MODS_TO_NEUTRALIZE { MOD_BIT(KC_LEFT_ALT), MOD_BIT(KC_LEFT_GUI) }
|
||||
|
||||
// Neutralize left alt, left GUI, right GUI and left Control+Shift
|
||||
#define MODS_TO_NEUTRALIZE { MOD_BIT(KC_LEFT_ALT), MOD_BIT(KC_LEFT_GUI), MOD_BIT(KC_RIGHT_GUI), MOD_BIT(KC_LEFT_CTRL)|MOD_BIT(KC_LEFT_SHIFT) }
|
||||
```
|
||||
|
||||
!> Do not use `MOD_xxx` constants like `MOD_LSFT` or `MOD_RALT`, since they're 5-bit packed bit-arrays while `MODS_TO_NEUTRALIZE` expects a list of 8-bit packed bit-arrays. Use `MOD_BIT(<kc>)` or `MOD_MASK_xxx` instead.
|
||||
|
@ -64,7 +64,7 @@ There are a number of functions (and variables) related to how you can use or ma
|
||||
| `layer_move(layer)` | Turns specified layer on, and all other layers off. |
|
||||
| `layer_on(layer)` | Turns specified layer on, leaves all other layers in existing state. |
|
||||
| `layer_off(layer)` | Turns specified layer off, leaves all other layers in existing state. |
|
||||
| `layer_invert(layer)` | Interverts/toggles the state of the specified layer |
|
||||
| `layer_invert(layer)` | Inverts/toggles the state of the specified layer |
|
||||
| `layer_or(layer_mask)` | Turns on layers based on matching bits between specifed layer and existing layer state. |
|
||||
| `layer_and(layer_mask)` | Turns on layers based on matching enabled bits between specifed layer and existing layer state. |
|
||||
| `layer_xor(layer_mask)` | Turns on layers based on non-matching bits between specifed layer and existing layer state. |
|
||||
@ -155,7 +155,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
|
||||
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) {
|
||||
if (current_layer > LAYER_CYCLE_END || current_layer < LAYER_CYCLE_START) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ There is basic support for addressable LED matrix lighting with the I2C IS31FL37
|
||||
|
||||
```make
|
||||
LED_MATRIX_ENABLE = yes
|
||||
LED_MATRIX_DRIVER = IS31FL3731
|
||||
LED_MATRIX_DRIVER = is31fl3731
|
||||
```
|
||||
|
||||
You can use between 1 and 4 IS31FL3731 IC's. Do not specify `LED_DRIVER_ADDR_<N>` defines for IC's that are not present on your keyboard. You can define the following items in `config.h`:
|
||||
@ -139,7 +139,7 @@ Currently only 4 drivers are supported, but it would be trivial to support for m
|
||||
Define these arrays listing all the LEDs in your `<keyboard>.c`:
|
||||
|
||||
```c
|
||||
const is31_led __flash g_is31_leds[LED_MATRIX_LED_COUNT] = {
|
||||
const is31_led PROGMEM g_is31_leds[LED_MATRIX_LED_COUNT] = {
|
||||
/* Refer to IS31 manual for these locations
|
||||
* driver
|
||||
* | LED address
|
||||
@ -158,7 +158,7 @@ Eg `#define ISSI_MANUAL_SCALING 3`
|
||||
Then Define the array listing all the LEDs you want to override in your `<keyboard>.c`:
|
||||
|
||||
```c
|
||||
const is31_led __flash g_is31_scaling[ISSI_MANUAL_SCALING] = {
|
||||
const is31_led PROGMEM g_is31_scaling[ISSI_MANUAL_SCALING] = {
|
||||
* LED Index
|
||||
* | Scaling
|
||||
* | | */
|
||||
@ -247,7 +247,6 @@ enum led_matrix_effects {
|
||||
LED_MATRIX_CYCLE_UP_DOWN, // Full gradient scrolling top to bottom
|
||||
LED_MATRIX_CYCLE_OUT_IN, // Full gradient scrolling out to in
|
||||
LED_MATRIX_DUAL_BEACON, // Full gradient spinning around center of keyboard
|
||||
#if defined(LED_MATRIX_KEYPRESSES) || defined(LED_MATRIX_KEYRELEASES)
|
||||
LED_MATRIX_SOLID_REACTIVE_SIMPLE, // Pulses keys hit then fades out
|
||||
LED_MATRIX_SOLID_REACTIVE_WIDE // Value pulses near a single key hit then fades out
|
||||
LED_MATRIX_SOLID_REACTIVE_MULTIWIDE // Value pulses near multiple key hits then fades out
|
||||
@ -257,7 +256,6 @@ enum led_matrix_effects {
|
||||
LED_MATRIX_SOLID_REACTIVE_MULTINEXUS // Value pulses away on the same column and row of multiple key hits then fades out
|
||||
LED_MATRIX_SOLID_SPLASH, // Value pulses away from a single key hit then fades out
|
||||
LED_MATRIX_SOLID_MULTISPLASH, // Value pulses away from multiple key hits then fades out
|
||||
#endif
|
||||
LED_MATRIX_WAVE_LEFT_RIGHT // Sine wave scrolling from left to right
|
||||
LED_MATRIX_WAVE_UP_DOWN // Sine wave scrolling from up to down
|
||||
LED_MATRIX_EFFECT_MAX
|
||||
@ -281,8 +279,6 @@ You can enable a single effect by defining `ENABLE_[EFFECT_NAME]` in your `confi
|
||||
|`#define ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT` |Enables `LED_MATRIX_WAVE_LEFT_RIGHT` |
|
||||
|`#define ENABLE_LED_MATRIX_WAVE_UP_DOWN` |Enables `LED_MATRIX_WAVE_UP_DOWN` |
|
||||
|
||||
?> These modes don't require any additional defines.
|
||||
|
||||
|Reactive Defines |Description |
|
||||
|-------------------------------------------------------|----------------------------------------------|
|
||||
|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE` |Enables `LED_MATRIX_SOLID_REACTIVE_SIMPLE` |
|
||||
@ -295,7 +291,7 @@ You can enable a single effect by defining `ENABLE_[EFFECT_NAME]` in your `confi
|
||||
|`#define ENABLE_LED_MATRIX_SOLID_SPLASH` |Enables `LED_MATRIX_SOLID_SPLASH` |
|
||||
|`#define ENABLE_LED_MATRIX_SOLID_MULTISPLASH` |Enables `LED_MATRIX_SOLID_MULTISPLASH` |
|
||||
|
||||
?> These modes also require the `LED_MATRIX_KEYPRESSES` or `LED_MATRIX_KEYRELEASES` define to be available.
|
||||
?> These modes introduce additional logic that can increase firmware size.
|
||||
|
||||
## Custom LED Matrix Effects :id=custom-led-matrix-effects
|
||||
|
||||
@ -361,9 +357,7 @@ For inspiration and examples, check out the built-in effects under `quantum/led_
|
||||
## Additional `config.h` Options :id=additional-configh-options
|
||||
|
||||
```c
|
||||
#define LED_MATRIX_KEYPRESSES // reacts to keypresses
|
||||
#define LED_MATRIX_KEYRELEASES // reacts to keyreleases (instead of keypresses)
|
||||
#define LED_MATRIX_FRAMEBUFFER_EFFECTS // enable framebuffer effects
|
||||
#define LED_MATRIX_KEYRELEASES // reactive effects respond to keyreleases (instead of keypresses)
|
||||
#define LED_MATRIX_TIMEOUT 0 // number of milliseconds to wait until led automatically turns off
|
||||
#define LED_DISABLE_WHEN_USB_SUSPENDED // turn off effects when suspended
|
||||
#define LED_MATRIX_LED_PROCESS_LIMIT (LED_MATRIX_LED_COUNT + 4) / 5 // limits the number of LEDs to process in an animation per task run (increases keyboard responsiveness)
|
||||
@ -373,7 +367,7 @@ For inspiration and examples, check out the built-in effects under `quantum/led_
|
||||
#define LED_MATRIX_DEFAULT_VAL LED_MATRIX_MAXIMUM_BRIGHTNESS // Sets the default brightness value, if none has been set
|
||||
#define LED_MATRIX_DEFAULT_SPD 127 // Sets the default animation speed, if none has been set
|
||||
#define LED_MATRIX_SPLIT { X, Y } // (Optional) For split keyboards, the number of LEDs connected on each half. X = left, Y = Right.
|
||||
// If LED_MATRIX_KEYPRESSES or LED_MATRIX_KEYRELEASES is enabled, you also will want to enable SPLIT_TRANSPORT_MIRROR
|
||||
// If reactive effects are enabled, you also will want to enable SPLIT_TRANSPORT_MIRROR
|
||||
```
|
||||
|
||||
## EEPROM storage :id=eeprom-storage
|
||||
@ -433,7 +427,7 @@ The EEPROM for it is currently shared with the RGB Matrix system (it's generally
|
||||
|
||||
### Indicators :id=indicators
|
||||
|
||||
If you want to set custom indicators, such as an LED for Caps Lock, or layer indication, you can use the `led_matrix_indicators_kb` or `led_matrix_indicators_user` function for that:
|
||||
If you want to set custom indicators, such as an LED for Caps Lock, or layer indication, then you can use the `led_matrix_indicators_kb` function on the keyboard level source file, or `led_matrix_indicators_user` function in the user `keymap.c`.
|
||||
```c
|
||||
bool led_matrix_indicators_kb(void) {
|
||||
if (!led_matrix_indicators_user()) {
|
||||
|
@ -27,7 +27,7 @@ You can define up to 32 macros in a `keymap.json` file, as used by [Configurator
|
||||
],
|
||||
[
|
||||
{"action":"tap", "keycodes": ["F1"]},
|
||||
{"action":"delay", "duration": "1000"},
|
||||
{"action":"delay", "duration": 1000},
|
||||
{"action":"tap", "keycodes": ["PGDN"]}
|
||||
]
|
||||
],
|
||||
|
@ -67,6 +67,7 @@ This is the default mode. You can adjust the cursor and scrolling acceleration u
|
||||
|`MOUSEKEY_TIME_TO_MAX` |30 |Time until maximum cursor speed is reached |
|
||||
|`MOUSEKEY_WHEEL_DELAY` |10 |Delay between pressing a wheel key and wheel movement |
|
||||
|`MOUSEKEY_WHEEL_INTERVAL` |80 |Time between wheel movements |
|
||||
|`MOUSEKEY_WHEEL_DELTA` |1 |Wheel movement step size |
|
||||
|`MOUSEKEY_WHEEL_MAX_SPEED` |8 |Maximum number of scroll steps per scroll action |
|
||||
|`MOUSEKEY_WHEEL_TIME_TO_MAX`|40 |Time until maximum scroll speed is reached |
|
||||
|
||||
@ -101,7 +102,7 @@ This is an extension of the accelerated mode. The kinetic mode uses a quadratic
|
||||
Tips:
|
||||
|
||||
* The smoothness of the cursor movement depends on the `MOUSEKEY_INTERVAL` setting. The shorter the interval is set the smoother the movement will be. Setting the value too low makes the cursor unresponsive. Lower settings are possible if the micro processor is fast enough. For example: At an interval of `8` milliseconds, `125` movements per second will be initiated. With a base speed of `1000` each movement will move the cursor by `8` pixels.
|
||||
* Mouse wheel movements are implemented differently from cursor movements. While it's okay for the cursor to move multiple pixels at once for the mouse wheel this would lead to jerky movements. Instead, the mouse wheel operates at step size `2`. Setting mouse wheel speed is done by adjusting the number of wheel movements per second.
|
||||
* Mouse wheel movements are implemented differently from cursor movements. While it's okay for the cursor to move multiple pixels at once for the mouse wheel this would lead to jerky movements. Instead, the mouse wheel operates at step size `1`. Setting mouse wheel speed is done by adjusting the number of wheel movements per second.
|
||||
|
||||
### Constant mode
|
||||
|
||||
|
@ -29,17 +29,17 @@ OLED_ENABLE = yes
|
||||
|
||||
|OLED Driver |Supported Device |
|
||||
|-------------------|------------------------------------|
|
||||
|SSD1306 (default) |For both SSD1306, SH1106, and SH1107|
|
||||
|`ssd1306` (default)|For both SSD1306, SH1106, and SH1107|
|
||||
|
||||
e.g.
|
||||
```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 |
|
||||
|`i2c` (default)| Uses I2C for communication with the OLED panel |
|
||||
|`spi` | Uses SPI for communication with the OLED panel |
|
||||
|
||||
e.g.
|
||||
```make
|
||||
|
@ -1,69 +1,156 @@
|
||||
# Raw HID
|
||||
# Raw HID :id=raw-hid
|
||||
|
||||
Raw HID allows for bidirectional communication between QMK and the host computer over an HID interface. This has many potential use cases, such as switching keymaps on the fly or changing RGB LED colors and modes.
|
||||
The Raw HID feature allows for bidirectional communication between QMK and the host computer over an HID interface. This has many potential use cases, such as switching keymaps on the fly or sending useful metrics like CPU/RAM usage.
|
||||
|
||||
There are two main components to getting raw HID working with your keyboard.
|
||||
In order to communicate with the keyboard using this feature, you will need to write a program that runs on the host. As such, some basic programming skills are required - more if you intend to implement complex behaviour.
|
||||
|
||||
## Keyboard firmware
|
||||
## Usage :id=usage
|
||||
|
||||
The implementation is fairly straightforward for the firmware.
|
||||
In your `rules.mk` add:
|
||||
Add the following to your `rules.mk`:
|
||||
|
||||
```make
|
||||
RAW_ENABLE = yes
|
||||
```
|
||||
|
||||
In your `keymap.c` include `"raw_hid.h"` and implement the following:
|
||||
## Basic Configuration :id=basic-configuration
|
||||
|
||||
By default, the HID Usage Page and Usage ID for the Raw HID interface are `0xFF60` and `0x61`. However, they can be changed if necessary by adding the following to your `config.h`:
|
||||
|
||||
|Define |Default |Description |
|
||||
|----------------|--------|---------------------------------------|
|
||||
|`RAW_USAGE_PAGE`|`0xFF60`|The usage page of the Raw HID interface|
|
||||
|`RAW_USAGE_ID` |`0x61` |The usage ID of the Raw HID interface |
|
||||
|
||||
## Sending Data to the Keyboard :id=sending-data-to-the-keyboard
|
||||
|
||||
To send data to the keyboard, you must first find a library for communicating with HID devices in the programming language of your choice. Here are some examples:
|
||||
|
||||
* **Node.js:** [node-hid](https://github.com/node-hid/node-hid)
|
||||
* **C/C++:** [hidapi](https://github.com/libusb/hidapi)
|
||||
* **Java:** [purejavahidapi](https://github.com/nyholku/purejavahidapi) and [hid4java](https://github.com/gary-rowe/hid4java)
|
||||
* **Python:** [pyhidapi](https://pypi.org/project/hid/)
|
||||
|
||||
Please refer to these libraries' own documentation for instructions on usage. Remember to close the device once you are finished with it!
|
||||
|
||||
Next, you will need to know the USB Vendor and Product IDs of the device. These can easily be found by looking at your keyboard's `info.json`, under the `usb` object (alternatively, you can also use Device Manager on Windows, System Information on macOS, or `lsusb` on Linux). For example, the Vendor ID for the Planck Rev 6 is `0x03A8`, and the Product ID is `0xA4F9`.
|
||||
|
||||
It's also a good idea to narrow down the list of potential HID devices the library may give you by filtering on the usage page and usage ID, to avoid accidentally opening the interface on the same device for the keyboard, or mouse, or media keys, etc.
|
||||
|
||||
Once you are able to open the HID device and send reports to it, it's time to handle them on the keyboard side. Implement the following function in your `keymap.c` and start coding:
|
||||
|
||||
```c
|
||||
void raw_hid_receive(uint8_t *data, uint8_t length) {
|
||||
// Your code goes here. data is the packet received from host.
|
||||
// Your code goes here
|
||||
// `data` is a pointer to the buffer containing the received HID report
|
||||
// `length` is the length of the report - always `RAW_EPSIZE`
|
||||
}
|
||||
```
|
||||
|
||||
The `"raw_hid.h"` header also declares `void raw_hid_send(uint8_t *data, uint8_t length);` which allows sending packets from keyboard to host. As an example, it can also be used for debugging when building your host application by returning all data back to the host.
|
||||
!> Because the HID specification does not support variable length reports, all reports in both directions must be exactly `RAW_EPSIZE` (currently 32) bytes long, regardless of actual payload length. However, variable length payloads can potentially be implemented on top of this by creating your own data structure that may span multiple reports.
|
||||
|
||||
## Receiving Data from the Keyboard :id=receiving-data-from-the-keyboard
|
||||
|
||||
If you need the keyboard to send data back to the host, simply call the `raw_hid_send()` function. It requires two arguments - a pointer to a 32-byte buffer containing the data you wish to send, and the length (which should always be `RAW_EPSIZE`).
|
||||
|
||||
The received report can then be handled in whichever way your HID library provides.
|
||||
|
||||
## Simple Example :id=simple-example
|
||||
|
||||
The following example reads the first byte of the received report from the host, and if it is an ASCII "A", responds with "B". `memset()` is used to fill the response buffer (which could still contain the previous response) with null bytes.
|
||||
|
||||
```c
|
||||
void raw_hid_receive(uint8_t *data, uint8_t length) {
|
||||
raw_hid_send(data, length);
|
||||
uint8_t response[length];
|
||||
memset(response, 0, length);
|
||||
response[0] = 'B';
|
||||
|
||||
if(data[0] == 'A') {
|
||||
raw_hid_send(response, length);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
These two functions send and receive packets of length `RAW_EPSIZE` bytes to and from the host (32 on LUFA/ChibiOS/V-USB, 64 on ATSAM).
|
||||
On the host side (here we are using Python and the `pyhidapi` library), the HID device is opened by enumerating the interfaces on the USB device, then filtering on the usage page and usage ID. Then, a report containing a single ASCII "A" (hex `0x41`) is constructed and sent.
|
||||
|
||||
Make sure to flash raw enabled firmware before proceeding with working on the host side.
|
||||
For demonstration purposes, the manufacturer and product strings of the device, along with the request and response, are also printed.
|
||||
|
||||
## Host (Windows/macOS/Linux)
|
||||
```python
|
||||
import sys
|
||||
import hid
|
||||
|
||||
This is the more complicated part as it will require some digging.
|
||||
vendor_id = 0x4335
|
||||
product_id = 0x0002
|
||||
|
||||
To connect your host computer to your keyboard with raw HID you need four pieces of information about your keyboard:
|
||||
usage_page = 0xFF60
|
||||
usage = 0x61
|
||||
report_length = 32
|
||||
|
||||
1. Vendor ID
|
||||
2. Product ID
|
||||
3. Usage Page
|
||||
4. Usage
|
||||
def get_raw_hid_interface():
|
||||
device_interfaces = hid.enumerate(vendor_id, product_id)
|
||||
raw_hid_interfaces = [i for i in device_interfaces if i['usage_page'] == usage_page and i['usage'] == usage]
|
||||
|
||||
The first two can easily be found in your keyboard's `config.h` in the keyboard's main directory under `VENDOR_ID` and `PRODUCT_ID`.
|
||||
if len(raw_hid_interfaces) == 0:
|
||||
return None
|
||||
|
||||
The final two can be overridden in your keyboard's `config.h` in the keyboard's main directory by redefining the values: `#define RAW_USAGE_PAGE 0xFF60` and `#define RAW_USAGE_ID 0x61`.
|
||||
interface = hid.Device(path=raw_hid_interfaces[0]['path'])
|
||||
|
||||
By default, **Usage Page** is `0xFF60` and **Usage** is `0x61`.
|
||||
print(f"Manufacturer: {interface.manufacturer}")
|
||||
print(f"Product: {interface.product}")
|
||||
|
||||
### Building your host
|
||||
return interface
|
||||
|
||||
You can build your host using any language that has an available HID implementation library if you don't wish to make your own. The ones we know of for popular languages are:
|
||||
def send_raw_report(data):
|
||||
interface = get_raw_hid_interface()
|
||||
|
||||
* Node: [node-hid](https://github.com/node-hid/node-hid).
|
||||
* C: [hidapi](https://github.com/libusb/hidapi).
|
||||
* Java: [purejavahidapi](https://github.com/nyholku/purejavahidapi) and [hid4java](https://github.com/gary-rowe/hid4java).
|
||||
* Python: [pyhidapi](https://pypi.org/project/hid/).
|
||||
if interface is None:
|
||||
print("No device found")
|
||||
sys.exit(1)
|
||||
|
||||
This is not an exhaustive cross-platform list but should get you started. There are no special requirements for using raw HID so any HID library should work.
|
||||
request_data = [0x00] * (report_length + 1) # First byte is Report ID
|
||||
request_data[1:len(data) + 1] = data
|
||||
request_report = bytes(request_data)
|
||||
|
||||
Now that you have all four pieces of information required to open HID interface to your keyboard. All you need to do is use your library's available functions to open the device with its ID parameters.
|
||||
print("Request:")
|
||||
print(request_report)
|
||||
|
||||
Note that Vendor ID and Product ID are not actually required to open the device. They are used only to filter to a specific device out of the many HID devices you have plugged in. Many libraries will give you the option to open the device using Product Name or Manufacturer Name instead, `node-hid` being a prime example. This will create issues for devices with builtin USB Hub or any extra HID interfaces where you will have multiple interfaces with the same name or from the same manufacturer. The Vendor ID together with Product ID create a unique designation to a single interface and will not exhibit this problem. Therefore, even if your library doesn't require you to, it is best to use them to avoid issues.
|
||||
Unlike Vendor ID and Product ID though, Usage Page and Usage are necessary for successful communication.
|
||||
try:
|
||||
interface.write(request_report)
|
||||
|
||||
It should go without saying that regardless of the library you're using, you should always make sure to close the interface when finished. Depending on the operating system and your particular environment there may be issues connecting to it again afterwards with another client or another instance of the same client if it's not explicitly closed.
|
||||
response_report = interface.read(report_length, timeout=1000)
|
||||
|
||||
print("Response:")
|
||||
print(response_report)
|
||||
finally:
|
||||
interface.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
send_raw_report([
|
||||
0x41
|
||||
])
|
||||
```
|
||||
|
||||
## API :id=api
|
||||
|
||||
### `void raw_hid_receive(uint8_t *data, uint8_t length)` :id=api-raw-hid-receive
|
||||
|
||||
Callback, invoked when a raw HID report has been received from the host.
|
||||
|
||||
#### Arguments :id=api-raw-hid-receive-arguments
|
||||
|
||||
- `uint8_t *data`
|
||||
A pointer to the received data. Always 32 bytes in length.
|
||||
- `uint8_t length`
|
||||
The length of the buffer. Always 32.
|
||||
|
||||
---
|
||||
|
||||
### `void raw_hid_send(uint8_t *data, uint8_t length)` :id=api-raw-hid-send
|
||||
|
||||
Send an HID report.
|
||||
|
||||
#### Arguments :id=api-raw-hid-send-arguments
|
||||
|
||||
- `uint8_t *data`
|
||||
A pointer to the data to send. Must always be 32 bytes in length.
|
||||
- `uint8_t length`
|
||||
The length of the buffer. Must always be 32.
|
||||
|
@ -12,7 +12,7 @@ There is basic support for addressable RGB matrix lighting with the I2C IS31FL37
|
||||
|
||||
```make
|
||||
RGB_MATRIX_ENABLE = yes
|
||||
RGB_MATRIX_DRIVER = IS31FL3731
|
||||
RGB_MATRIX_DRIVER = is31fl3731
|
||||
```
|
||||
|
||||
You can use between 1 and 4 IS31FL3731 IC's. Do not specify `DRIVER_ADDR_<N>` defines for IC's that are not present on your keyboard. You can define the following items in `config.h`:
|
||||
@ -76,7 +76,7 @@ There is basic support for addressable RGB matrix lighting with the I2C IS31FL37
|
||||
|
||||
```make
|
||||
RGB_MATRIX_ENABLE = yes
|
||||
RGB_MATRIX_DRIVER = IS31FL3733
|
||||
RGB_MATRIX_DRIVER = is31fl3733
|
||||
```
|
||||
|
||||
You can use between 1 and 4 IS31FL3733 IC's. Do not specify `DRIVER_ADDR_<N>` defines for IC's that are not present on your keyboard. You can define the following items in `config.h`:
|
||||
@ -162,7 +162,7 @@ There is basic support for addressable RGB matrix lighting with the I2C IS31FL37
|
||||
|
||||
```make
|
||||
RGB_MATRIX_ENABLE = yes
|
||||
RGB_MATRIX_DRIVER = IS31FL3736
|
||||
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.
|
||||
|
||||
@ -238,7 +238,7 @@ There is basic support for addressable RGB matrix lighting with the I2C IS31FL37
|
||||
|
||||
```make
|
||||
RGB_MATRIX_ENABLE = yes
|
||||
RGB_MATRIX_DRIVER = IS31FL3737
|
||||
RGB_MATRIX_DRIVER = is31fl3737
|
||||
```
|
||||
You can use between 1 and 4 IS31FL3737 IC's. Do not specify `DRIVER_ADDR_<N>` defines for IC's that are not present on your keyboard.
|
||||
|
||||
@ -386,7 +386,7 @@ Currently only 4 drivers are supported, but it would be trivial to support for m
|
||||
Define these arrays listing all the LEDs in your `<keyboard>.c`:
|
||||
|
||||
```c
|
||||
const is31_led __flash g_is31_leds[RGB_MATRIX_LED_COUNT] = {
|
||||
const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT] = {
|
||||
/* Refer to IS31 manual for these locations
|
||||
* driver
|
||||
* | R location
|
||||
@ -406,7 +406,7 @@ Eg `#define ISSI_MANUAL_SCALING 3`
|
||||
Then Define the array listing all the LEDs you want to override in your `<keyboard>.c`:
|
||||
|
||||
```c
|
||||
const is31_led __flash g_is31_scaling[ISSI_MANUAL_SCALING] = {
|
||||
const is31_led PROGMEM g_is31_scaling[ISSI_MANUAL_SCALING] = {
|
||||
* LED Index
|
||||
* | R scaling
|
||||
* | | G scaling
|
||||
@ -428,7 +428,7 @@ There is basic support for addressable RGB matrix lighting with a WS2811/WS2812{
|
||||
|
||||
```make
|
||||
RGB_MATRIX_ENABLE = yes
|
||||
RGB_MATRIX_DRIVER = WS2812
|
||||
RGB_MATRIX_DRIVER = ws2812
|
||||
```
|
||||
|
||||
Configure the hardware via your `config.h`:
|
||||
@ -450,7 +450,7 @@ There is basic support for APA102 based addressable LED strands. To enable it, a
|
||||
|
||||
```make
|
||||
RGB_MATRIX_ENABLE = yes
|
||||
RGB_MATRIX_DRIVER = APA102
|
||||
RGB_MATRIX_DRIVER = apa102
|
||||
```
|
||||
|
||||
Configure the hardware via your `config.h`:
|
||||
@ -470,7 +470,7 @@ There is basic support for addressable RGB matrix lighting with the SPI AW20216
|
||||
|
||||
```make
|
||||
RGB_MATRIX_ENABLE = yes
|
||||
RGB_MATRIX_DRIVER = AW20216
|
||||
RGB_MATRIX_DRIVER = aw20216
|
||||
```
|
||||
|
||||
You can use up to 2 AW20216 IC's. Do not specify `DRIVER_<N>_xxx` defines for IC's that are not present on your keyboard. You can define the following items in `config.h`:
|
||||
@ -650,11 +650,8 @@ enum rgb_matrix_effects {
|
||||
RGB_MATRIX_PIXEL_FRACTAL, // Single hue fractal filled keys pulsing horizontally out to edges
|
||||
RGB_MATRIX_PIXEL_FLOW, // Pulsing RGB flow along LED wiring with random hues
|
||||
RGB_MATRIX_PIXEL_RAIN, // Randomly light keys with random hues
|
||||
#if defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS)
|
||||
RGB_MATRIX_TYPING_HEATMAP, // How hot is your WPM!
|
||||
RGB_MATRIX_DIGITAL_RAIN, // That famous computer simulation
|
||||
#endif
|
||||
#if defined(RGB_MATRIX_KEYPRESSES) || defined(RGB_MATRIX_KEYRELEASES)
|
||||
RGB_MATRIX_SOLID_REACTIVE_SIMPLE, // Pulses keys hit to hue & value then fades value out
|
||||
RGB_MATRIX_SOLID_REACTIVE, // Static single hue, pulses keys hit to shifted hue then fades to current hue
|
||||
RGB_MATRIX_SOLID_REACTIVE_WIDE // Hue & value pulse near a single key hit then fades value out
|
||||
@ -667,7 +664,6 @@ enum rgb_matrix_effects {
|
||||
RGB_MATRIX_MULTISPLASH, // Full gradient & value pulse away from multiple key hits then fades value out
|
||||
RGB_MATRIX_SOLID_SPLASH, // Hue & value pulse away from a single key hit then fades value out
|
||||
RGB_MATRIX_SOLID_MULTISPLASH, // Hue & value pulse away from multiple key hits then fades value out
|
||||
#endif
|
||||
RGB_MATRIX_EFFECT_MAX
|
||||
};
|
||||
```
|
||||
@ -707,14 +703,12 @@ You can enable a single effect by defining `ENABLE_[EFFECT_NAME]` in your `confi
|
||||
|`#define ENABLE_RGB_MATRIX_PIXEL_FLOW` |Enables `RGB_MATRIX_PIXEL_FLOW` |
|
||||
|`#define ENABLE_RGB_MATRIX_PIXEL_RAIN` |Enables `RGB_MATRIX_PIXEL_RAIN` |
|
||||
|
||||
?> These modes don't require any additional defines.
|
||||
|
||||
|Framebuffer Defines |Description |
|
||||
|------------------------------------------------------|----------------------------------------------|
|
||||
|`#define ENABLE_RGB_MATRIX_TYPING_HEATMAP` |Enables `RGB_MATRIX_TYPING_HEATMAP` |
|
||||
|`#define ENABLE_RGB_MATRIX_DIGITAL_RAIN` |Enables `RGB_MATRIX_DIGITAL_RAIN` |
|
||||
|
||||
?> These modes also require the `RGB_MATRIX_FRAMEBUFFER_EFFECTS` define to be available.
|
||||
?> These modes introduce additional logic that can increase firmware size.
|
||||
|
||||
|Reactive Defines |Description |
|
||||
|------------------------------------------------------|----------------------------------------------|
|
||||
@ -731,7 +725,7 @@ You can enable a single effect by defining `ENABLE_[EFFECT_NAME]` in your `confi
|
||||
|`#define ENABLE_RGB_MATRIX_SOLID_SPLASH` |Enables `RGB_MATRIX_SOLID_SPLASH` |
|
||||
|`#define ENABLE_RGB_MATRIX_SOLID_MULTISPLASH` |Enables `RGB_MATRIX_SOLID_MULTISPLASH` |
|
||||
|
||||
?> These modes also require the `RGB_MATRIX_KEYPRESSES` or `RGB_MATRIX_KEYRELEASES` define to be available.
|
||||
?> These modes introduce additional logic that can increase firmware size.
|
||||
|
||||
|
||||
### RGB Matrix Effect Typing Heatmap :id=rgb-matrix-effect-typing-heatmap
|
||||
@ -872,9 +866,7 @@ These are defined in [`color.h`](https://github.com/qmk/qmk_firmware/blob/master
|
||||
## Additional `config.h` Options :id=additional-configh-options
|
||||
|
||||
```c
|
||||
#define RGB_MATRIX_KEYPRESSES // reacts to keypresses
|
||||
#define RGB_MATRIX_KEYRELEASES // reacts to keyreleases (instead of keypresses)
|
||||
#define RGB_MATRIX_FRAMEBUFFER_EFFECTS // enable framebuffer effects
|
||||
#define RGB_MATRIX_KEYRELEASES // reactive effects respond to keyreleases (instead of keypresses)
|
||||
#define RGB_MATRIX_TIMEOUT 0 // number of milliseconds to wait until rgb automatically turns off
|
||||
#define RGB_DISABLE_WHEN_USB_SUSPENDED // turn off effects when suspended
|
||||
#define RGB_MATRIX_LED_PROCESS_LIMIT (RGB_MATRIX_LED_COUNT + 4) / 5 // limits the number of LEDs to process in an animation per task run (increases keyboard responsiveness)
|
||||
@ -887,7 +879,7 @@ These are defined in [`color.h`](https://github.com/qmk/qmk_firmware/blob/master
|
||||
#define RGB_MATRIX_DEFAULT_SPD 127 // Sets the default animation speed, if none has been set
|
||||
#define RGB_MATRIX_DISABLE_KEYCODES // disables control of rgb matrix by keycodes (must use code functions to control the feature)
|
||||
#define RGB_MATRIX_SPLIT { X, Y } // (Optional) For split keyboards, the number of LEDs connected on each half. X = left, Y = Right.
|
||||
// If RGB_MATRIX_KEYPRESSES or RGB_MATRIX_KEYRELEASES is enabled, you also will want to enable SPLIT_TRANSPORT_MIRROR
|
||||
// If reactive effects are enabled, you also will want to enable SPLIT_TRANSPORT_MIRROR
|
||||
#define RGB_TRIGGER_ON_KEYDOWN // Triggers RGB keypress events on key down. This makes RGB control feel more responsive. This may cause RGB to not function properly on some boards
|
||||
```
|
||||
|
||||
@ -964,7 +956,7 @@ The EEPROM for it is currently shared with the LED Matrix system (it's generally
|
||||
|
||||
### Indicators :id=indicators
|
||||
|
||||
If you want to set custom indicators, such as an LED for Caps Lock, or layer indication, you can use the `rgb_matrix_indicators_kb` or `rgb_matrix_indicators_user` function for that:
|
||||
If you want to set custom indicators, such as an LED for Caps Lock, or layer indication, then you can use the `rgb_matrix_indicators_kb` function on the keyboard level source file, or `rgb_matrix_indicators_user` function in the user `keymap.c`.
|
||||
```c
|
||||
bool rgb_matrix_indicators_kb(void) {
|
||||
if (!rgb_matrix_indicators_user()) {
|
||||
|
@ -28,7 +28,7 @@ For APA102 LEDs, add the following to your `rules.mk`:
|
||||
|
||||
```make
|
||||
RGBLIGHT_ENABLE = yes
|
||||
RGBLIGHT_DRIVER = APA102
|
||||
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.
|
||||
@ -90,7 +90,7 @@ Your RGB lighting can be configured by placing these `#define`s in your `config.
|
||||
|
||||
|Define |Default |Description |
|
||||
|---------------------------|----------------------------|---------------------------------------------------------------------------------------------------------------------------|
|
||||
|`RGBLIGHT_HUE_STEP` |`10` |The number of steps to cycle through the hue by |
|
||||
|`RGBLIGHT_HUE_STEP` |`8` |The number of steps to cycle through the hue by |
|
||||
|`RGBLIGHT_SAT_STEP` |`17` |The number of steps to increment the saturation by |
|
||||
|`RGBLIGHT_VAL_STEP` |`17` |The number of steps to increment the brightness by |
|
||||
|`RGBLIGHT_LIMIT_VAL` |`255` |The maximum brightness level |
|
||||
@ -524,37 +524,6 @@ By defining `RGBLIGHT_LED_MAP` as in the example below, you can specify the LED
|
||||
```
|
||||
<img src="https://user-images.githubusercontent.com/2170248/55743725-08ad7a80-5a6e-11e9-83ed-126a2b0209fc.JPG" alt="simple mapped" width="50%"/>
|
||||
|
||||
For keyboards that use the RGB LEDs as a backlight for each key, you can also define it as in the example below.
|
||||
|
||||
```c
|
||||
// config.h
|
||||
|
||||
#define RGBLED_NUM 30
|
||||
|
||||
/* RGB LED Conversion macro from physical array to electric array */
|
||||
#define LED_LAYOUT( \
|
||||
L00, L01, L02, L03, L04, L05, \
|
||||
L10, L11, L12, L13, L14, L15, \
|
||||
L20, L21, L22, L23, L24, L25, \
|
||||
L30, L31, L32, L33, L34, L35, \
|
||||
L40, L41, L42, L43, L44, L45 ) \
|
||||
{ \
|
||||
L05, L04, L03, L02, L01, L00, \
|
||||
L10, L11, L12, L13, L14, L15, \
|
||||
L25, L24, L23, L22, L21, L20, \
|
||||
L30, L31, L32, L33, L34, L35, \
|
||||
L46, L45, L44, L43, L42, L41 \
|
||||
}
|
||||
|
||||
/* RGB LED logical order map */
|
||||
/* Top->Bottom, Right->Left */
|
||||
#define RGBLIGHT_LED_MAP LED_LAYOUT( \
|
||||
25, 20, 15, 10, 5, 0, \
|
||||
26, 21, 16, 11, 6, 1, \
|
||||
27, 22, 17, 12, 7, 2, \
|
||||
28, 23, 18, 13, 8, 3, \
|
||||
29, 24, 19, 14, 9, 4 )
|
||||
|
||||
```
|
||||
## Clipping Range
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Send String
|
||||
# Send String :id=send-string
|
||||
|
||||
The Send String API is part of QMK's macro system. It allows for sequences of keystrokes to be sent automatically.
|
||||
|
||||
@ -6,7 +6,7 @@ The full ASCII character set is supported, along with all of the keycodes in the
|
||||
|
||||
?> Unicode characters are **not** supported with this API -- see the [Unicode](feature_unicode.md) feature instead.
|
||||
|
||||
## Usage
|
||||
## Usage :id=usage
|
||||
|
||||
Send String is enabled by default, so there is usually no need for any special setup. However, if it is disabled, add the following to your `rules.mk`:
|
||||
|
||||
@ -14,7 +14,7 @@ Send String is enabled by default, so there is usually no need for any special s
|
||||
SEND_STRING_ENABLE = yes
|
||||
```
|
||||
|
||||
## Basic Configuration
|
||||
## Basic Configuration :id=basic-configuration
|
||||
|
||||
Add the following to your `config.h`:
|
||||
|
||||
@ -23,7 +23,7 @@ Add the following to your `config.h`:
|
||||
|`SENDSTRING_BELL`|*Not defined* |If the [Audio](feature_audio.md) feature is enabled, the `\a` character (ASCII `BEL`) will beep the speaker.|
|
||||
|`BELL_SOUND` |`TERMINAL_SOUND`|The song to play when the `\a` character is encountered. By default, this is an eighth note of C5. |
|
||||
|
||||
## Keycodes
|
||||
## Keycodes :id=keycodes
|
||||
|
||||
The Send String functions accept C string literals, but specific keycodes can be injected with the below macros. All of the keycodes in the [Basic Keycode range](keycodes_basic.md) are supported (as these are the only ones that will actually be sent to the host), but with an `X_` prefix instead of `KC_`.
|
||||
|
||||
@ -44,13 +44,13 @@ The following characters are also mapped to their respective keycodes for conven
|
||||
|`\t` |`\x1B`|`TAB`|`KC_TAB` |
|
||||
| |`\x7F`|`DEL`|`KC_DELETE` |
|
||||
|
||||
### Language Support
|
||||
### Language Support :id=language-support
|
||||
|
||||
By default, Send String assumes your OS keyboard layout is set to US ANSI. If you are using a different keyboard layout, you can [override the lookup tables used to convert ASCII characters to keystrokes](reference_keymap_extras.md#sendstring-support).
|
||||
|
||||
## Examples
|
||||
## Examples :id=examples
|
||||
|
||||
### Hello World
|
||||
### Hello World :id=example-hello-world
|
||||
|
||||
A simple custom keycode which types out "Hello, world!" and the Enter key when pressed.
|
||||
|
||||
@ -70,7 +70,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
|
||||
}
|
||||
```
|
||||
|
||||
### Keycode Injection
|
||||
### Keycode Injection :id=example-keycode-injection
|
||||
|
||||
This example types out opening and closing curly braces, then taps the left arrow key to move the cursor between the two.
|
||||
|
||||
@ -84,26 +84,26 @@ This example types Ctrl+A, then Ctrl+C, without releasing Ctrl.
|
||||
SEND_STRING(SS_LCTL("ac"));
|
||||
```
|
||||
|
||||
## API
|
||||
## API :id=api
|
||||
|
||||
### `void send_string(const char *string)`
|
||||
### `void send_string(const char *string)` :id=api-send-string
|
||||
|
||||
Type out a string of ASCII characters.
|
||||
|
||||
This function simply calls `send_string_with_delay(string, 0)`.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-send-string-arguments
|
||||
|
||||
- `const char *string`
|
||||
The string to type out.
|
||||
|
||||
---
|
||||
|
||||
### `void send_string_with_delay(const char *string, uint8_t interval)`
|
||||
### `void send_string_with_delay(const char *string, uint8_t interval)` :id=api-send-string-with-delay
|
||||
|
||||
Type out a string of ASCII characters, with a delay between each character.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-send-string-with-delay-arguments
|
||||
|
||||
- `const char *string`
|
||||
The string to type out.
|
||||
@ -112,26 +112,26 @@ Type out a string of ASCII characters, with a delay between each character.
|
||||
|
||||
---
|
||||
|
||||
### `void send_string_P(const char *string)`
|
||||
### `void send_string_P(const char *string)` :id=api-send-string-p
|
||||
|
||||
Type out a PROGMEM string of ASCII characters.
|
||||
|
||||
On ARM devices, this function is simply an alias for `send_string_with_delay(string, 0)`.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-send-string-p-arguments
|
||||
|
||||
- `const char *string`
|
||||
The string to type out.
|
||||
|
||||
---
|
||||
|
||||
### `void send_string_with_delay_P(const char *string, uint8_t interval)`
|
||||
### `void send_string_with_delay_P(const char *string, uint8_t interval)` :id=api-send-string-with-delay-p
|
||||
|
||||
Type out a PROGMEM string of ASCII characters, with a delay between each character.
|
||||
|
||||
On ARM devices, this function is simply an alias for `send_string_with_delay(string, interval)`.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-send-string-with-delay-p-arguments
|
||||
|
||||
- `const char *string`
|
||||
The string to type out.
|
||||
@ -140,76 +140,76 @@ On ARM devices, this function is simply an alias for `send_string_with_delay(str
|
||||
|
||||
---
|
||||
|
||||
### `void send_char(char ascii_code)`
|
||||
### `void send_char(char ascii_code)` :id=api-send-char
|
||||
|
||||
Type out an ASCII character.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-send-char-arguments
|
||||
|
||||
- `char ascii_code`
|
||||
The character to type.
|
||||
|
||||
---
|
||||
|
||||
### `void send_dword(uint32_t number)`
|
||||
### `void send_dword(uint32_t number)` :id=api-send-dword
|
||||
|
||||
Type out an eight digit (unsigned 32-bit) hexadecimal value.
|
||||
|
||||
The format is `[0-9a-f]{8}`, eg. `00000000` through `ffffffff`.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-send-dword-arguments
|
||||
|
||||
- `uint32_t number`
|
||||
The value to type, from 0 to 4,294,967,295.
|
||||
|
||||
---
|
||||
|
||||
### `void send_word(uint16_t number)`
|
||||
### `void send_word(uint16_t number)` :id=api-send-word
|
||||
|
||||
Type out a four digit (unsigned 16-bit) hexadecimal value.
|
||||
|
||||
The format is `[0-9a-f]{4}`, eg. `0000` through `ffff`.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-send-word-arguments
|
||||
|
||||
- `uint16_t number`
|
||||
The value to type, from 0 to 65,535.
|
||||
|
||||
---
|
||||
|
||||
### `void send_byte(uint8_t number)`
|
||||
### `void send_byte(uint8_t number)` :id=api-send-bytes
|
||||
|
||||
Type out a two digit (8-bit) hexadecimal value.
|
||||
|
||||
The format is `[0-9a-f]{2}`, eg. `00` through `ff`.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-send-byte-arguments
|
||||
|
||||
- `uint8_t number`
|
||||
The value to type, from 0 to 255.
|
||||
|
||||
---
|
||||
|
||||
### `void send_nibble(uint8_t number)`
|
||||
### `void send_nibble(uint8_t number)` :id=api-send-nibble
|
||||
|
||||
Type out a single hexadecimal digit.
|
||||
|
||||
The format is `[0-9a-f]{1}`, eg. `0` through `f`.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-send-nibble-arguments
|
||||
|
||||
- `uint8_t number`
|
||||
The value to type, from 0 to 15.
|
||||
|
||||
---
|
||||
|
||||
### `void tap_random_base64(void)`
|
||||
### `void tap_random_base64(void)` :id=api-tap-random-base64
|
||||
|
||||
Type a pseudorandom character from the set `A-Z`, `a-z`, `0-9`, `+` and `/`.
|
||||
|
||||
---
|
||||
|
||||
### `SEND_STRING(string)`
|
||||
### `SEND_STRING(string)` :id=api-send-string-macro
|
||||
|
||||
Shortcut macro for `send_string_with_delay_P(PSTR(string), 0)`.
|
||||
|
||||
@ -217,7 +217,7 @@ On ARM devices, this define evaluates to `send_string_with_delay(string, 0)`.
|
||||
|
||||
---
|
||||
|
||||
### `SEND_STRING_DELAY(string, interval)`
|
||||
### `SEND_STRING_DELAY(string, interval)` :id=api-send-string-delay-macro
|
||||
|
||||
Shortcut macro for `send_string_with_delay_P(PSTR(string), interval)`.
|
||||
|
||||
|
@ -28,7 +28,7 @@ After this, you'll want to use the `tap_dance_actions` array to specify what act
|
||||
* `ACTION_TAP_DANCE_LAYER_TOGGLE(kc, layer)`: Sends the `kc` keycode when tapped once, or toggles the state of `layer`. (this functions like the `TG` layer keycode).
|
||||
* `ACTION_TAP_DANCE_FN(fn)`: Calls the specified function - defined in the user keymap - with the final tap count of the tap dance action.
|
||||
* `ACTION_TAP_DANCE_FN_ADVANCED(on_each_tap_fn, on_dance_finished_fn, on_dance_reset_fn)`: Calls the first specified function - defined in the user keymap - on every tap, the second function when the dance action finishes (like the previous option), and the last function when the tap dance action resets.
|
||||
|
||||
* `ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(on_each_tap_fn, on_each_release_fn, on_dance_finished_fn, on_dance_reset_fn)`: This macro is identical to `ACTION_TAP_DANCE_FN_ADVANCED` with the addition of `on_each_release_fn` which is invoked every time the key for the tap dance is released. It is worth noting that `on_each_release_fn` will still be called even when the key is released after the dance finishes (e.g. if the key is released after being pressed and held for longer than the `TAPPING_TERM`).
|
||||
|
||||
The first option is enough for a lot of cases, that just want dual roles. For example, `ACTION_TAP_DANCE_DOUBLE(KC_SPC, KC_ENT)` will result in `Space` being sent on single-tap, `Enter` otherwise.
|
||||
|
||||
|
@ -1,48 +1,77 @@
|
||||
# Unicode Support
|
||||
# Unicode :id=unicode
|
||||
|
||||
Unicode characters can be input straight from your keyboard! There are some limitations, however.
|
||||
With a little help from your OS, practically any Unicode character can be input using your keyboard.
|
||||
|
||||
In order to enable Unicode support on your keyboard, you will need to do the following:
|
||||
## Caveats :id=caveats
|
||||
|
||||
1. Choose one of three supported Unicode implementations: [Basic Unicode](#basic-unicode), [Unicode Map](#unicode-map), [UCIS](#ucis).
|
||||
2. Find which [input mode](#input-modes) is the best match for your operating system and setup.
|
||||
3. [Set](#setting-the-input-mode) the appropriate input mode (or modes) in your configuration.
|
||||
4. Add Unicode keycodes to your keymap.
|
||||
There are some limitations to this feature. Because there is no "standard" method of Unicode input across all operating systems, each of them require their own setup process on both the host *and* in the firmware, which may involve installation of additional software. This also means Unicode input will not "just work" when the keyboard is plugged into another device.
|
||||
|
||||
## Usage :id=usage
|
||||
|
||||
## 1. Methods :id=methods
|
||||
The core Unicode API can be used purely programmatically. However, there are also additional subsystems which build on top of it and come with keycodes to make things easier. See below for more details.
|
||||
|
||||
QMK supports three different methods for enabling Unicode input and adding Unicode characters to your keymap. Each has its pros and cons in terms of flexibility and ease of use. Choose the one that best fits your use case.
|
||||
Add the following to your keymap's `rules.mk`:
|
||||
|
||||
The Basic method should be enough for most users. However, if you need a wider range of supported characters (including emoji, rare symbols etc.), you should use Unicode Map.
|
||||
```make
|
||||
UNICODE_COMMON = yes
|
||||
```
|
||||
|
||||
<br>
|
||||
## Basic Configuration :id=basic-configuration
|
||||
|
||||
### 1.1. Basic Unicode :id=basic-unicode
|
||||
Add the following to your `config.h`:
|
||||
|
||||
The easiest to use method, albeit somewhat limited. It stores Unicode characters as keycodes in the keymap itself, so it only supports code points up to `0x7FFF`. This covers characters for most modern languages (including East Asian), as well as symbols, but it doesn't cover emoji.
|
||||
|Define |Default |Description |
|
||||
|------------------------|------------------|--------------------------------------------------------------------------------|
|
||||
|`UNICODE_KEY_MAC` |`KC_LEFT_ALT` |The key to hold when beginning a Unicode sequence with the macOS input mode |
|
||||
|`UNICODE_KEY_LNX` |`LCTL(LSFT(KC_U))`|The key to tap when beginning a Unicode sequence with the Linux input mode |
|
||||
|`UNICODE_KEY_WINC` |`KC_RIGHT_ALT` |The key to hold when beginning a Unicode sequence with the WinCompose input mode|
|
||||
|`UNICODE_SELECTED_MODES`|`-1` |A comma separated list of input modes for cycling through |
|
||||
|`UNICODE_CYCLE_PERSIST` |`true` |Whether to persist the current Unicode input mode to EEPROM |
|
||||
|`UNICODE_TYPE_DELAY` |`10` |The amount of time to wait, in milliseconds, between Unicode sequence keystrokes|
|
||||
|
||||
Add the following to your `rules.mk`:
|
||||
### Audio Feedback :id=audio-feedback
|
||||
|
||||
If you have the [Audio](feature_audio.md) feature enabled on your board, you can configure it to play sounds when the input mode is changed.
|
||||
|
||||
Add the following to your `config.h`:
|
||||
|
||||
|Define |Default|Description |
|
||||
|-------------------|-------|-----------------------------------------------------------|
|
||||
|`UNICODE_SONG_MAC` |*n/a* |The song to play when the macOS input mode is selected |
|
||||
|`UNICODE_SONG_LNX` |*n/a* |The song to play when the Linux input mode is selected |
|
||||
|`UNICODE_SONG_BSD` |*n/a* |The song to play when the BSD input mode is selected |
|
||||
|`UNICODE_SONG_WIN` |*n/a* |The song to play when the Windows input mode is selected |
|
||||
|`UNICODE_SONG_WINC`|*n/a* |The song to play when the WinCompose input mode is selected|
|
||||
|
||||
## Input Subsystems :id=input-subsystems
|
||||
|
||||
Each of these subsystems have their own pros and cons in terms of flexibility and ease of use. Choose the one that best fits your needs.
|
||||
|
||||
<!-- tabs:start -->
|
||||
|
||||
### ** Basic **
|
||||
|
||||
This is the easiest to use, albeit somewhat limited. It supports code points up to `U+7FFF`, which covers characters for most modern languages (including East Asian), as well as many symbols, but does not include emoji.
|
||||
|
||||
To enable Basic Unicode, add the following to your `rules.mk`:
|
||||
|
||||
```make
|
||||
UNICODE_ENABLE = yes
|
||||
```
|
||||
|
||||
Then add `UC(c)` keycodes to your keymap, where _c_ is the code point of the desired character (preferably in hexadecimal, up to 4 digits long). For example, `UC(0x40B)` will output [Ћ](https://unicode-table.com/en/040B/), and `UC(0x30C4)` will output [ツ](https://unicode-table.com/en/30C4).
|
||||
You can then add `UC(c)` keycodes to your keymap, where *c* is the code point of the desired character (in hexadecimal - the `U+` prefix will not work). For example, `UC(0x40B)` will output [Ћ](https://unicode-table.com/en/040B/), and `UC(0x30C4)` will output [ツ](https://unicode-table.com/en/30C4).
|
||||
|
||||
<br>
|
||||
### ** Unicode Map **
|
||||
|
||||
### 1.2. Unicode Map :id=unicode-map
|
||||
Unicode Map supports all possible code points (up to `U+10FFFF`). Here, the code points are stored in a separate mapping table (which may contain at most 16,384 entries), instead of directly in the keymap.
|
||||
|
||||
In addition to standard character ranges, this method also covers emoji, ancient scripts, rare symbols etc. In fact, all possible code points (up to `0x10FFFF`) are supported. Here, Unicode characters are stored in a separate mapping table. You need to maintain a `unicode_map` array in your keymap file, which may contain at most 16384 entries.
|
||||
|
||||
Add the following to your `rules.mk`:
|
||||
To enable Unicode Map, add the following to your `rules.mk`:
|
||||
|
||||
```make
|
||||
UNICODEMAP_ENABLE = yes
|
||||
```
|
||||
|
||||
Then add `X(i)` keycodes to your keymap, where _i_ is the desired character's index in the mapping table. This can be a numeric value, but it's recommended to keep the indices in an enum and access them by name.
|
||||
Then, you will need to create a mapping table in your `keymap.c`, and (optionally) an enum for naming the array indices, like so:
|
||||
|
||||
```c
|
||||
enum unicode_names {
|
||||
@ -51,242 +80,373 @@ enum unicode_names {
|
||||
SNEK
|
||||
};
|
||||
|
||||
const uint32_t unicode_map[] PROGMEM = {
|
||||
const uint32_t PROGMEM unicode_map[] = {
|
||||
[BANG] = 0x203D, // ‽
|
||||
[IRONY] = 0x2E2E, // ⸮
|
||||
[SNEK] = 0x1F40D, // 🐍
|
||||
};
|
||||
```
|
||||
|
||||
Then you can use `X(BANG)`, `X(SNEK)` etc. in your keymap.
|
||||
Finally, add `UM(i)` keycodes to your keymap, where *i* is an index into the `unicode_map[]` array. If you defined the enum above, you can use those names instead, for example `UM(BANG)` or `UM(SNEK)`.
|
||||
|
||||
#### Lower and Upper Case
|
||||
#### Lower and Upper Case Pairs :id=unicodemap-pairs
|
||||
|
||||
Characters often come in lower and upper case pairs, such as å and Å. To make inputting these characters easier, you can use `XP(i, j)` in your keymap, where _i_ and _j_ are the mapping table indices of the lower and upper case character, respectively. If you're holding down Shift or have Caps Lock turned on when you press the key, the second (upper case) character will be inserted; otherwise, the first (lower case) version will appear.
|
||||
Some writing systems have lowercase and uppercase variants of each character, such as å and Å. To make inputting these characters easier, you can use the `UP(i, j)` keycode in your keymap, where *i* and *j* are the mapping table indices of the lowercase and uppercase characters, respectively. If you're holding down Shift or have Caps Lock turned on when you press the key, the uppercase character will be inserted; otherwise, the lowercase character will be inserted.
|
||||
|
||||
This is most useful when creating a keymap for an international layout with special characters. Instead of having to put the lower and upper case versions of a character on separate keys, you can have them both on the same key by using `XP()`. This helps blend Unicode keys in with regular alphas.
|
||||
```c
|
||||
const uint32_t PROGMEM unicode_map[] = {
|
||||
[AE_LOWER] = 0x00E6, // æ
|
||||
[AE_UPPER] = 0x00C6, // Æ
|
||||
};
|
||||
```
|
||||
|
||||
Due to keycode size constraints, _i_ and _j_ can each only refer to one of the first 128 characters in your `unicode_map`. In other words, 0 ≤ _i_ ≤ 127 and 0 ≤ _j_ ≤ 127. This is enough for most use cases, but if you'd like to customize the index calculation, you can override the [`unicodemap_index()`](https://github.com/qmk/qmk_firmware/blob/71f640d47ee12c862c798e1f56392853c7b1c1a8/quantum/process_keycode/process_unicodemap.c#L36) function. This also allows you to, say, check Ctrl instead of Shift/Caps.
|
||||
This is most useful when creating a keymap for an international layout with special characters. Instead of having to put the lower and upper case versions of a character on separate keys, you can have them both on the same key. This helps blend Unicode keys in with regular keycodes.
|
||||
|
||||
<br>
|
||||
Due to keycode size constraints, *i* and *j* can each only refer to one of the first 128 characters in your `unicode_map`. In other words, 0 ≤ *i* ≤ 127 and 0 ≤ *j* ≤ 127.
|
||||
|
||||
### 1.3. UCIS :id=ucis
|
||||
### ** UCIS **
|
||||
|
||||
This method also supports all possible code points. As with the Unicode Map method, you need to maintain a mapping table in your keymap file. However, there are no built-in keycodes for this feature — you have to create a custom keycode or function that invokes this functionality.
|
||||
As with Unicode Map, the UCIS method also supports all possible code points, and requires the use of a mapping table. However, it works much differently - Unicode characters are input by replacing a typed mnemonic.
|
||||
|
||||
Add the following to your `rules.mk`:
|
||||
To enable UCIS, add the following to your keymap's `rules.mk`:
|
||||
|
||||
```make
|
||||
UCIS_ENABLE = yes
|
||||
```
|
||||
|
||||
Then define a table like this in your keymap file:
|
||||
Then, create a mapping table in your `keymap.c`:
|
||||
|
||||
```c
|
||||
const ucis_symbol_t ucis_symbol_table[] = UCIS_TABLE(
|
||||
UCIS_SYM("poop", 0x1F4A9), // 💩
|
||||
UCIS_SYM("rofl", 0x1F923), // 🤣
|
||||
UCIS_SYM("cuba", 0x1F1E8, 0x1F1FA), // 🇨🇺
|
||||
UCIS_SYM("ukr", 0x1F1FA, 0x1F1E6), // 🇺🇦
|
||||
UCIS_SYM("look", 0x0CA0, 0x005F, 0x0CA0) // ಠ_ಠ
|
||||
);
|
||||
```
|
||||
|
||||
By default, each table entry may be up to 3 code points long. This number can be changed by adding `#define UCIS_MAX_CODE_POINTS n` to your `config.h` file.
|
||||
By default, each table entry may be up to three code points long. This can be changed by adding `#define UCIS_MAX_CODE_POINTS n` to your keymap's `config.h`.
|
||||
|
||||
To use UCIS input, call `ucis_start()`. Then, type the mnemonic for the character (such as "rofl") and hit Space, Enter or Esc. QMK should erase the "rofl" text and insert the laughing emoji.
|
||||
To invoke UCIS input, the `ucis_start()` function must first be called (for example, in a custom "Unicode" keycode). Then, type the mnemonic for the mapping table entry (such as "rofl"), and hit Space or Enter. The "rofl" text will be backspaced and the emoji inserted.
|
||||
|
||||
#### Customization
|
||||
<!-- tabs:end -->
|
||||
|
||||
There are several functions that you can define in your keymap to customize the functionality of this feature.
|
||||
## Input Modes :id=input-modes
|
||||
|
||||
* `void ucis_start_user(void)` – This runs when you call the "start" function, and can be used to provide feedback. By default, it types out a keyboard emoji.
|
||||
* `void ucis_success(uint8_t symbol_index)` – This runs when the input has matched something and has completed. By default, it doesn't do anything.
|
||||
* `void ucis_symbol_fallback (void)` – This runs when the input doesn't match anything. By default, it falls back to trying that input as a Unicode code.
|
||||
Unicode input works by typing a sequence of characters, similar to a macro. However, since this sequence depends on your OS, you will need to prepare both your host machine and QMK to recognise and send the correct Unicode input sequences respectively.
|
||||
|
||||
You can find the default implementations of these functions in [`process_ucis.c`](https://github.com/qmk/qmk_firmware/blob/master/quantum/process_keycode/process_ucis.c).
|
||||
|
||||
|
||||
## 2. Input Modes :id=input-modes
|
||||
|
||||
Unicode input in QMK works by inputting a sequence of characters to the OS, sort of like a macro. Unfortunately, the way this is done differs for each platform. Specifically, each platform requires a different combination of keys to trigger Unicode input. Therefore, a corresponding input mode has to be set in QMK.
|
||||
|
||||
The following input modes are available:
|
||||
|
||||
* **`UNICODE_MODE_MACOS`**: macOS built-in Unicode hex input. Supports code points up to `0x10FFFF` (all possible code points).
|
||||
|
||||
To enable, go to _System Preferences > Keyboard > Input Sources_, add _Unicode Hex Input_ to the list (it's under _Other_), then activate it from the input dropdown in the Menu Bar.
|
||||
By default, this mode uses the left Option key (`KC_LALT`) for Unicode input, but this can be changed by defining [`UNICODE_KEY_MAC`](#input-key-configuration) with a different keycode.
|
||||
|
||||
!> Using the _Unicode Hex Input_ input source may disable some Option-based shortcuts, such as Option+Left and Option+Right.
|
||||
|
||||
* **`UNICODE_MODE_LINUX`**: Linux built-in IBus Unicode input. Supports code points up to `0x10FFFF` (all possible code points).
|
||||
|
||||
Enabled by default and works almost anywhere on IBus-enabled distros. Without IBus, this mode works under GTK apps, but rarely anywhere else.
|
||||
By default, this mode uses Ctrl+Shift+U (`LCTL(LSFT(KC_U))`) to start Unicode input, but this can be changed by defining [`UNICODE_KEY_LNX`](#input-key-configuration) with a different keycode. This might be required for IBus versions ≥1.5.15, where Ctrl+Shift+U behavior is consolidated into Ctrl+Shift+E.
|
||||
|
||||
Users who wish support in non-GTK apps without IBus may need to resort to a more indirect method, such as creating a custom keyboard layout ([more on this method](#custom-linux-layout)).
|
||||
|
||||
* **`UNICODE_MODE_WINDOWS`**: _(not recommended)_ Windows built-in hex numpad Unicode input. Supports code points up to `0xFFFF`.
|
||||
|
||||
To enable, create a registry key under `HKEY_CURRENT_USER\Control Panel\Input Method` of type `REG_SZ` called `EnableHexNumpad` and set its value to `1`. This can be done from the Command Prompt by running `reg add "HKCU\Control Panel\Input Method" -v EnableHexNumpad -t REG_SZ -d 1` with administrator privileges. Reboot afterwards.
|
||||
This mode is not recommended because of reliability and compatibility issues; use the `UNICODE_MODE_WINCOMPOSE` mode instead.
|
||||
|
||||
* **`UNICODE_MODE_BSD`**: _(non implemented)_ Unicode input under BSD. Not implemented at this time. If you're a BSD user and want to help add support for it, please [open an issue on GitHub](https://github.com/qmk/qmk_firmware/issues).
|
||||
|
||||
* **`UNICODE_MODE_WINCOMPOSE`**: Windows Unicode input using [WinCompose](https://github.com/samhocevar/wincompose). As of v0.9.0, supports code points up to `0x10FFFF` (all possible code points).
|
||||
|
||||
To enable, install the [latest release](https://github.com/samhocevar/wincompose/releases/latest). Once installed, WinCompose will automatically run on startup. This mode works reliably under all version of Windows supported by the app.
|
||||
By default, this mode uses right Alt (`KC_RALT`) as the Compose key, but this can be changed in the WinCompose settings and by defining [`UNICODE_KEY_WINC`](#input-key-configuration) with a different keycode.
|
||||
|
||||
|
||||
## 3. Setting the Input Mode :id=setting-the-input-mode
|
||||
|
||||
To set your desired input mode, add the following define to your `config.h`:
|
||||
To set the list of enabled input modes, add the `UNICODE_SELECTED_MODES` define to your keymap's `config.h`, for example:
|
||||
|
||||
```c
|
||||
#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX
|
||||
// or
|
||||
#define UNICODE_SELECTED_MODES UNICODE_MODE_MAC, UNICODE_MODE_WINCOMPOSE
|
||||
```
|
||||
|
||||
This example sets the board's default input mode to `UNICODE_MODE_LINUX`. You can replace this with `UNICODE_MODE_MACOS`, `UNICODE_MODE_WINCOMPOSE`, or any of the other modes listed [above](#input-modes). The board will automatically use the selected mode on startup, unless you manually switch to another mode (see [below](#keycodes)).
|
||||
These modes can then be cycled through using the `UC_NEXT` and `UC_PREV` keycodes. You can also switch to any input mode, even if it is not specified in `UNICODE_SELECTED_MODES`, using their respective keycodes.
|
||||
|
||||
You can also select multiple input modes, which allows you to easily cycle through them using the `UC_NEXT`/`UC_PREV` keycodes.
|
||||
If your keyboard has working EEPROM, it will remember the last used input mode and continue using it on the next power up. This can be disabled by defining `UNICODE_CYCLE_PERSIST` to `false`.
|
||||
|
||||
```c
|
||||
#define UNICODE_SELECTED_MODES UNICODE_MODE_MACOS, UNICODE_MODE_LINUX, UNICODE_MODE_WINCOMPOSE
|
||||
<!-- tabs:start -->
|
||||
|
||||
### ** macOS **
|
||||
|
||||
**Mode Name:** `UNICODE_MODE_MAC`
|
||||
|
||||
macOS has built-in support for Unicode input as its own input source. It supports all possible code points by way of surrogate pairs for code points above `U+FFFF`.
|
||||
|
||||
To enable, go to **System Preferences → Keyboard → Input Sources**, then add Unicode Hex Input to the list (under Other), and activate it from the input dropdown in the menu bar. Note that this may disable some Option-based shortcuts such as Option+Left and Option+Right.
|
||||
|
||||
### ** Linux (IBus) **
|
||||
|
||||
**Mode Name:** `UNICODE_MODE_LINUX`
|
||||
|
||||
For Linux distros with IBus, Unicode input is enabled by default, supports all possible code points, and works almost anywhere. Without IBus, it works under GTK apps, but rarely anywhere else.
|
||||
|
||||
Users who would like support in non-GTK apps without IBus may need to resort to a more indirect method, such as creating a custom keyboard layout.
|
||||
|
||||
### ** Windows (WinCompose) **
|
||||
|
||||
**Mode Name:** `UNICODE_MODE_WINCOMPOSE`
|
||||
|
||||
This mode requires a third-party tool called [WinCompose](https://github.com/samhocevar/wincompose). It supports all possible code points, and is the recommended input mode for Windows.
|
||||
|
||||
To enable, install the [latest release from GitHub](https://github.com/samhocevar/wincompose/releases/latest). Once installed, it will automatically run on startup. This works reliably under all versions of Windows supported by WinCompose.
|
||||
|
||||
### ** Windows (HexNumpad) **
|
||||
|
||||
**Mode Name:** `UNICODE_MODE_WINDOWS`
|
||||
|
||||
!> This input mode is *not* the "Alt code" system. Alt codes are not Unicode; they instead follow [the Windows-1252 character set](https://en.wikipedia.org/wiki/Alt_code).
|
||||
|
||||
This is Windows' built-in hex numpad Unicode input mode. It only supports code points up to `U+FFFF`, and is not recommended due to reliability and compatibility issues.
|
||||
|
||||
To enable, run the following as an administrator, then reboot:
|
||||
|
||||
```
|
||||
reg add "HKCU\Control Panel\Input Method" -v EnableHexNumpad -t REG_SZ -d 1
|
||||
```
|
||||
|
||||
Note that the values are separated by commas. The board will remember the last used input mode and will continue using it on next power-up. You can disable this and force it to always start with the first mode in the list by adding `#define UNICODE_CYCLE_PERSIST false` to your `config.h`.
|
||||
### ** Emacs **
|
||||
|
||||
#### Keycodes
|
||||
**Mode Name:** `UNICODE_MODE_EMACS`
|
||||
|
||||
You can switch the input mode at any time by using the following keycodes. Adding these to your keymap allows you to quickly switch to a specific input mode, including modes not listed in `UNICODE_SELECTED_MODES`.
|
||||
Emacs supports code point input with the `insert-char` command.
|
||||
|
||||
|Keycode |Alias |Input Mode |Description |
|
||||
|----------------------------|---------|-------------------------|-----------------------------------------------------------------------------|
|
||||
|`QK_UNICODE_MODE_NEXT` |`UC_NEXT`|Next in list |Cycle through selected modes, reverse direction when Shift is held |
|
||||
|`QK_UNICODE_MODE_PREVIOUS` |`UC_PREV`|Prev in list |Cycle through selected modes in reverse, forward direction when Shift is held|
|
||||
|`QK_UNICODE_MODE_MACOS` |`UC_MAC` |`UNICODE_MODE_MACOS` |Switch to macOS input |
|
||||
|`QK_UNICODE_MODE_LINUX` |`UC_LINX`|`UNICODE_MODE_LINUX` |Switch to Linux input |
|
||||
|`QK_UNICODE_MODE_WINDOWS` |`UC_WIN` |`UNICODE_MODE_WINDOWS` |Switch to Windows input |
|
||||
|`QK_UNICODE_MODE_BSD` |`UC_BSD` |`UNICODE_MODE_BSD` |Switch to BSD input _(not implemented)_ |
|
||||
|`QK_UNICODE_MODE_WINCOMPOSE`|`UC_WINC`|`UNICODE_MODE_WINCOMPOSE`|Switch to Windows input using WinCompose |
|
||||
|`QK_UNICODE_MODE_EMACS` |`UC_EMAC`|`UNICODE_MODE_EMACS` |Switch to emacs (`C-x-8 RET`) |
|
||||
### ** BSD **
|
||||
|
||||
You can also switch the input mode by calling `set_unicode_input_mode(x)` in your code, where _x_ is one of the above input mode constants (e.g. `UNICODE_MODE_LINUX`).
|
||||
**Mode Name:** `UNICODE_MODE_BSD`
|
||||
|
||||
?> Using `UNICODE_SELECTED_MODES` is preferable to calling `set_unicode_input_mode()` in `matrix_init_user()` or similar functions, since it's better integrated into the Unicode system and has the added benefit of avoiding unnecessary writes to EEPROM.
|
||||
Not currently implemented. If you're a BSD user and want to contribute support for this input mode, please [feel free](contributing.md)!
|
||||
|
||||
#### Audio Feedback
|
||||
<!-- tabs:end -->
|
||||
|
||||
If you have the [Audio feature](feature_audio.md) enabled on the board, you can set melodies to be played when you press the above keys. That way you can have some audio feedback when switching input modes.
|
||||
## Keycodes :id=keycodes
|
||||
|
||||
For instance, you can add these definitions to your `config.h` file:
|
||||
|Key |Aliases |Description |
|
||||
|----------------------------|---------|----------------------------------------------------------------|
|
||||
|`UC(c)` | |Send Unicode code point `c`, up to `0x7FFF` |
|
||||
|`UM(i)` | |Send Unicode code point at index `i` in `unicode_map` |
|
||||
|`UP(i, j)` | |Send Unicode code point at index `i`, or `j` if Shift/Caps is on|
|
||||
|`QK_UNICODE_MODE_NEXT` |`UC_NEXT`|Cycle through selected input modes |
|
||||
|`QK_UNICODE_MODE_PREVIOUS` |`UC_PREV`|Cycle through selected input modes in reverse |
|
||||
|`QK_UNICODE_MODE_MACOS` |`UC_MAC` |Switch to macOS input |
|
||||
|`QK_UNICODE_MODE_LINUX` |`UC_LINX`|Switch to Linux input |
|
||||
|`QK_UNICODE_MODE_WINDOWS` |`UC_WIN` |Switch to Windows input |
|
||||
|`QK_UNICODE_MODE_BSD` |`UC_BSD` |Switch to BSD input (not implemented) |
|
||||
|`QK_UNICODE_MODE_WINCOMPOSE`|`UC_WINC`|Switch to Windows input using WinCompose |
|
||||
|`QK_UNICODE_MODE_EMACS` |`UC_EMAC`|Switch to emacs (`C-x-8 RET`) |
|
||||
|
||||
```c
|
||||
#define UNICODE_SONG_MAC AUDIO_ON_SOUND
|
||||
#define UNICODE_SONG_LNX UNICODE_LINUX
|
||||
#define UNICODE_SONG_BSD TERMINAL_SOUND
|
||||
#define UNICODE_SONG_WIN UNICODE_WINDOWS
|
||||
#define UNICODE_SONG_WINC UNICODE_WINDOWS
|
||||
```
|
||||
## API :id=api
|
||||
|
||||
### `uint8_t get_unicode_input_mode(void)` :id=api-get-unicode-input-mode
|
||||
|
||||
## Additional Customization
|
||||
Get the current Unicode input mode.
|
||||
|
||||
Because Unicode is a large and versatile feature, there are a number of options you can customize to make it work better on your system.
|
||||
#### Return Value :id=api-get-unicode-input-mode-return-value
|
||||
|
||||
### Start and Finish Input Functions
|
||||
The currently active Unicode input mode.
|
||||
|
||||
The functions for starting and finishing Unicode input on your platform can be overridden locally. Possible uses include customizing input mode behavior if you don't use the default keys, or adding extra visual/audio feedback to Unicode input.
|
||||
---
|
||||
|
||||
* `void unicode_input_start(void)` – This sends the initial sequence that tells your platform to enter Unicode input mode. For example, it holds the left Alt key followed by Num+ on Windows, and presses the `UNICODE_KEY_LNX` combination (default: Ctrl+Shift+U) on Linux.
|
||||
* `void unicode_input_finish(void)` – This is called to exit Unicode input mode, for example by pressing Space or releasing the Alt key.
|
||||
### `void set_unicode_input_mode(uint8_t mode)` :id=api-set-unicode-input-mode
|
||||
|
||||
You can find the default implementations of these functions in [`process_unicode_common.c`](https://github.com/qmk/qmk_firmware/blob/master/quantum/process_keycode/process_unicode_common.c).
|
||||
Set the Unicode input mode.
|
||||
|
||||
### Input Mode Callbacks
|
||||
#### Arguments :id=api-set-unicode-input-mode-arguments
|
||||
|
||||
There are callbacks functions available that are called whenever the unicode input mode changes. The new input mode is passed to the function.
|
||||
- `uint8_t mode`
|
||||
The input mode to set.
|
||||
|
||||
|Callback |Description |
|
||||
|---------------------------------------------------|-----------------------------------------------------|
|
||||
| `unicode_input_mode_set_kb(uint8_t input_mode)` | Callback for unicode input mode set, for keyboard. |
|
||||
| `unicode_input_mode_set_user(uint8_t input_mode)` | Callback for unicode input mode set, for users. |
|
||||
---
|
||||
|
||||
This feature can be used, for instance, to implement LED indicators for the current unicode input mode.
|
||||
### `void unicode_input_mode_step(void)` : id=api-unicode-input-mode-step
|
||||
|
||||
### Input Key Configuration
|
||||
Change to the next Unicode input mode.
|
||||
|
||||
You can customize the keys used to trigger Unicode input for macOS, Linux and WinCompose by adding corresponding defines to your `config.h`. The default values match the platforms' default settings, so you shouldn't need to change this unless Unicode input isn't working, or you want to use a different key (e.g. in order to free up left or right Alt).
|
||||
---
|
||||
|
||||
|Define |Type |Default |Example |
|
||||
|------------------|----------|------------------|-------------------------------------------|
|
||||
|`UNICODE_KEY_MAC` |`uint8_t` |`KC_LALT` |`#define UNICODE_KEY_MAC KC_RALT` |
|
||||
|`UNICODE_KEY_LNX` |`uint16_t`|`LCTL(LSFT(KC_U))`|`#define UNICODE_KEY_LNX LCTL(LSFT(KC_E))`|
|
||||
|`UNICODE_KEY_WINC`|`uint8_t` |`KC_RALT` |`#define UNICODE_KEY_WINC KC_RGUI` |
|
||||
### `void unicode_input_mode_step_reverse(void)` : id=api-unicode-input-mode-step-reverse
|
||||
|
||||
Change to the previous Unicode input mode.
|
||||
|
||||
## Sending Unicode Strings
|
||||
---
|
||||
|
||||
QMK provides several functions that allow you to send Unicode input to the host programmatically:
|
||||
### `void unicode_input_mode_set_user(uint8_t input_mode)` :id=api-unicode-input-mode-set-user
|
||||
|
||||
### `send_unicode_string()`
|
||||
User-level callback, invoked when the input mode is changed.
|
||||
|
||||
This function is much like `send_string()`, but it allows you to input UTF-8 characters directly. It supports all code points, provided the selected input mode also supports it. Make sure your `keymap.c` file is formatted using UTF-8 encoding.
|
||||
#### Arguments :id=api-unicode-input-mode-set-user-arguments
|
||||
|
||||
```c
|
||||
send_unicode_string("(ノಠ痊ಠ)ノ彡┻━┻");
|
||||
```
|
||||
- `uint8_t input_mode`
|
||||
The new input mode.
|
||||
|
||||
Example uses include sending Unicode strings when a key is pressed, as described in [Macros](feature_macros.md).
|
||||
---
|
||||
|
||||
## Additional Language Support
|
||||
### `void unicode_input_mode_set_kb(uint8_t input_mode)` :id=api-unicode-input-mode-set-kb
|
||||
|
||||
In `quantum/keymap_extras`, you'll see various language files — these work the same way as the ones for alternative layouts such as Colemak or BÉPO. When you include one of these language headers, you gain access to keycodes specific to that language / national layout. Such keycodes are defined by a 2-letter country/language code, followed by an underscore and a 4-letter abbreviation of the character to which the key corresponds. For example, including `keymap_french.h` and using `FR_UGRV` in your keymap will output `ù` when typed on a system with a native French AZERTY layout.
|
||||
Keyboard-level callback, invoked when the input mode is changed.
|
||||
|
||||
If the primary system layout you use on your machine is different from US ANSI, using these language-specific keycodes can help your QMK keymaps better match what will actually be output on the screen. However, keep in mind that these keycodes are just aliases for the corresponding default US keycodes under the hood, and that the HID protocol used by keyboards is itself inherently based on US ANSI.
|
||||
#### Arguments :id=api-unicode-input-mode-set-kb-arguments
|
||||
|
||||
- `uint8_t input_mode`
|
||||
The new input mode.
|
||||
|
||||
## International Characters on Windows
|
||||
---
|
||||
|
||||
### AutoHotkey
|
||||
### `void unicode_input_start(void)` :id=api-unicode-input-start
|
||||
|
||||
The method does not require Unicode support in the keyboard itself but instead depends on [AutoHotkey](https://autohotkey.com) running in the background.
|
||||
Begin the Unicode input sequence. The exact behavior depends on the currently selected input mode:
|
||||
|
||||
First you need to select a modifier combination that is not in use by any of your programs.
|
||||
Ctrl+Alt+Win is not used very widely and should therefore be perfect for this.
|
||||
There is a macro defined for a mod-tab combo `LCAG_T`.
|
||||
Add this mod-tab combo to a key on your keyboard, e.g.: `LCAG_T(KC_TAB)`.
|
||||
This makes the key behave like a tab key if pressed and released immediately but changes it to the modifier if used with another key.
|
||||
- **macOS**: Hold `UNICODE_KEY_MAC`
|
||||
- **Linux**: Tap `UNICODE_KEY_LNX`
|
||||
- **WinCompose**: Tap `UNICODE_KEY_WINC`, then U
|
||||
- **HexNumpad**: Hold Left Alt, then tap Numpad +
|
||||
- **Emacs**: Tap Ctrl+X, then 8, then Enter
|
||||
|
||||
In the default script of AutoHotkey you can define custom hotkeys.
|
||||
This function is weakly defined, and can be overridden in user code.
|
||||
|
||||
<^<!<#a::Send, ä
|
||||
<^<!<#<+a::Send, Ä
|
||||
---
|
||||
|
||||
The hotkeys above are for the combination CtrlAltGui and CtrlAltGuiShift plus the letter a.
|
||||
AutoHotkey inserts the Text right of `Send, ` when this combination is pressed.
|
||||
### `void unicode_input_finish(void)` :id=api-unicode-input-finish
|
||||
|
||||
### US International
|
||||
Complete the Unicode input sequence. The exact behavior depends on the currently selected input mode:
|
||||
|
||||
If you enable the US International layout on the system, it will use punctuation to accent the characters. For instance, typing "\`a" will result in à.
|
||||
You can find details on how to enable this [here](https://support.microsoft.com/en-us/help/17424/windows-change-keyboard-layout).
|
||||
- **macOS**: Release `UNICODE_KEY_MAC`
|
||||
- **Linux**: Tap Space
|
||||
- **WinCompose**: Tap Enter
|
||||
- **HexNumpad**: Release Left Alt
|
||||
- **Emacs**: Tap Enter
|
||||
|
||||
## Software keyboard layout on Linux :id=custom-linux-layout
|
||||
This function is weakly defined, and can be overridden in user code.
|
||||
|
||||
This method does not require Unicode support on the keyboard itself but instead uses a custom keyboard layout for Xorg. This is how special characters are inserted by regular keyboards. This does not require IBus and works in practically all software. Help on creating a custom layout can be found [here](https://www.linux.com/news/creating-custom-keyboard-layouts-x11-using-xkb/), [here](http://karols.github.io/blog/2013/11/18/creating-custom-keyboard-layouts-for-linux/) and [here](https://wiki.archlinux.org/index.php/X_keyboard_extension). An example of how you could edit the `us` layout to gain 🤣 on `RALT(KC_R)`:
|
||||
---
|
||||
|
||||
Edit the keyboard layout file `/usr/share/X11/xkb/symbols/us`.
|
||||
### `void unicode_input_cancel(void)` :id=api-unicode-input-cancel
|
||||
|
||||
Inside `xkb_symbols "basic" {`, add `include "level3(ralt_switch)"`.
|
||||
Cancel the Unicode input sequence. The exact behavior depends on the currently selected input mode:
|
||||
|
||||
Find the line defining the R key and add an entry to the list, making it look like this:
|
||||
```
|
||||
key <AD04> { [ r, R, U1F923 ] };
|
||||
```
|
||||
- **macOS**: Release `UNICODE_KEY_MAC`
|
||||
- **Linux**: Tap Escape
|
||||
- **WinCompose**: Tap Escape
|
||||
- **HexNumpad**: Release Left Alt
|
||||
- **Emacs**: Tap Ctrl+G
|
||||
|
||||
Save the file and run the command `setxkbmap us` to reload the layout.
|
||||
This function is weakly defined, and can be overridden in user code.
|
||||
|
||||
You can define one custom character for key defined in the layout, and another if you populate the fourth layer. Additional layers up to 8th are also possible.
|
||||
---
|
||||
|
||||
This method is specific to the computer on which you set the custom layout. The custom keys will be available only when Xorg is running. To avoid accidents, you should always reload the layout using `setxkbmap`, otherwise an invalid layout could prevent you from logging into your system, locking you out.
|
||||
### `void register_unicode(uint32_t code_point)` :id=api-register-unicode
|
||||
|
||||
Input a single Unicode character. A surrogate pair will be sent if required by the input mode.
|
||||
|
||||
#### Arguments :id=api-register-unicode-arguments
|
||||
|
||||
- `uint32_t code_point`
|
||||
The code point of the character to send.
|
||||
|
||||
---
|
||||
|
||||
### `void send_unicode_string(const char *str)` :id=api-send-unicode-string
|
||||
|
||||
Send a string containing Unicode characters.
|
||||
|
||||
#### Arguments :id=api-send-unicode-string-arguments
|
||||
|
||||
- `const char *str`
|
||||
The string to send.
|
||||
|
||||
---
|
||||
|
||||
### `uint8_t unicodemap_index(uint16_t keycode)` :id=api-unicodemap-index
|
||||
|
||||
Get the index into the `unicode_map` array for the given keycode, respecting shift state for pair keycodes.
|
||||
|
||||
#### Arguments :id=api-unicodemap-index-arguments
|
||||
|
||||
- `uint16_t keycode`
|
||||
The Unicode Map keycode to get the index of.
|
||||
|
||||
#### Return Value :id=api-unicodemap-index-return-value
|
||||
|
||||
An index into the `unicode_map` array.
|
||||
|
||||
---
|
||||
|
||||
### `uint32_t unicodemap_get_code_point(uint8_t index)` :id=api-unicodemap-get-code-point
|
||||
|
||||
Get the code point for the given index in the `unicode_map` array.
|
||||
|
||||
#### Arguments :id=unicodemap-get-code-point-arguments
|
||||
|
||||
- `uint8_t index`
|
||||
The index into the `unicode_map` array.
|
||||
|
||||
#### Return Value :id=unicodemap-get-code-point-return-value
|
||||
|
||||
A Unicode code point value.
|
||||
|
||||
---
|
||||
|
||||
### `void register_unicodemap(uint8_t index)` :id=api-register-unicodemap
|
||||
|
||||
Send the code point for the given index in the `unicode_map` array.
|
||||
|
||||
#### Arguments :id=api-register-unicodemap-arguments
|
||||
|
||||
- `uint8_t index`
|
||||
The index into the `unicode_map` array.
|
||||
|
||||
---
|
||||
|
||||
### `void ucis_start(void)` :id=api-ucis-start
|
||||
|
||||
Begin the input sequence.
|
||||
|
||||
---
|
||||
|
||||
### `bool ucis_active(void)` :id=api-ucis-active
|
||||
|
||||
Whether UCIS is currently active.
|
||||
|
||||
#### Return Value :id=api-ucis-active-return-value
|
||||
|
||||
`true` if UCIS is active.
|
||||
|
||||
---
|
||||
|
||||
### `uint8_t ucis_count(void)` :id=api-ucis-count
|
||||
|
||||
Get the number of characters in the input sequence buffer.
|
||||
|
||||
#### Return Value :id=api-ucis-count-return-value
|
||||
|
||||
The current input sequence buffer length.
|
||||
|
||||
---
|
||||
|
||||
### `bool ucis_add(uint16_t keycode)` :id=api-ucis-add
|
||||
|
||||
Add the given keycode to the input sequence buffer.
|
||||
|
||||
#### Arguments :id=api-ucis-add-arguments
|
||||
|
||||
- `uint16_t keycode`
|
||||
The keycode to add. Must be between `KC_A` and `KC_Z`, or `KC_1` and `KC_0`.
|
||||
|
||||
#### Return Value :id=api-ucis-add-return-value
|
||||
|
||||
`true` if the keycode was added.
|
||||
|
||||
---
|
||||
|
||||
### `bool ucis_remove_last(void)` :id=api-ucis-remove-last
|
||||
|
||||
Remove the last character from the input sequence buffer.
|
||||
|
||||
#### Return Value :id=api-ucis-remove-last
|
||||
|
||||
`true` if the sequence was not empty.
|
||||
|
||||
---
|
||||
|
||||
### `void ucis_finish(void)` :id=api-ucis-finish
|
||||
|
||||
Mark the input sequence as complete, and attempt to match.
|
||||
|
||||
---
|
||||
|
||||
### `void ucis_cancel(void)` :id=api-ucis-cancel
|
||||
|
||||
Cancel the input sequence.
|
||||
|
||||
---
|
||||
|
||||
### `void register_ucis(void)` :id=api-register-ucis
|
||||
|
||||
Send the code point(s) for the given UCIS index.
|
||||
|
||||
#### Arguments :id=api-register-ucis-arguments
|
||||
|
||||
- `uint8_t index`
|
||||
The index into the UCIS symbol table.
|
||||
|
@ -322,6 +322,29 @@ Flashing sequence:
|
||||
3. Flash a .bin file
|
||||
4. Reset the device into application mode (may be done automatically)
|
||||
|
||||
## WB32 DFU
|
||||
|
||||
Some keyboards produced for several commercial brands (GMMK, Akko, MonsGeek, Inland) use this bootloader. The `wb32-dfu-updater` utility is bundled with [QMK MSYS](https://msys.qmk.fm/) and [Glorious's build of QMK Toolbox](https://www.gloriousgaming.com/blogs/guides-resources/gmmk-2-qmk-installation-guide). If neither of these flashing methods is available for your OS, you will likely need to [compile the CLI version from source](https://github.com/WestberryTech/wb32-dfu-updater).
|
||||
|
||||
The `info.json` setting for this bootloader is `wb32-dfu`.
|
||||
|
||||
Compatible flashers:
|
||||
|
||||
* [Glorious's build of QMK Toolbox](https://www.gloriousgaming.com/blogs/guides-resources/gmmk-2-qmk-installation-guide) (recommended GUI)
|
||||
* [wb32-dfu-updater_cli](https://github.com/WestberryTech/wb32-dfu-updater) / `:flash` target in QMK (recommended command line)
|
||||
```
|
||||
wb32-dfu-updater_cli -t -s 0x8000000 -D <filename>
|
||||
```
|
||||
|
||||
Flashing sequence:
|
||||
|
||||
1. Enter the bootloader using any of the following methods:
|
||||
* Tap the `QK_BOOT` keycode
|
||||
* Press the `RESET` button on the PCB
|
||||
2. Wait for the OS to detect the device
|
||||
3. Flash a .bin file
|
||||
4. Reset the device into application mode (may be done automatically)
|
||||
|
||||
## tinyuf2
|
||||
|
||||
Keyboards may opt into supporting the tinyuf2 bootloader. This is currently only supported on F303/F401/F411.
|
||||
|
@ -109,7 +109,7 @@ This allows you to send Unicode characters using `UC(<code point>)` in your keym
|
||||
|
||||
`UNICODEMAP_ENABLE`
|
||||
|
||||
This allows you to send Unicode characters using `X(<map index>)` in your keymap. You will need to maintain a mapping table in your keymap file. All possible code points (up to `0x10FFFF`) are supported.
|
||||
This allows you to send Unicode characters using `UM(<map index>)` in your keymap. You will need to maintain a mapping table in your keymap file. All possible code points (up to `0x10FFFF`) are supported.
|
||||
|
||||
`UCIS_ENABLE`
|
||||
|
||||
|
@ -72,7 +72,7 @@ Configuration-wise, you'll need to set up the peripheral as per your MCU's datas
|
||||
|
||||
The following configuration values depend on the specific MCU in use.
|
||||
|
||||
### I2Cv1 :id=i2cv1
|
||||
### I2Cv1 :id=arm-configuration-i2cv1
|
||||
|
||||
* STM32F1xx
|
||||
* STM32F2xx
|
||||
@ -88,7 +88,7 @@ See [this page](https://www.playembedded.org/blog/stm32-i2c-chibios/#7_I2Cv1_con
|
||||
|`I2C1_CLOCK_SPEED` |`100000` |
|
||||
|`I2C1_DUTY_CYCLE` |`STD_DUTY_CYCLE`|
|
||||
|
||||
### I2Cv2 :id=i2cv2
|
||||
### I2Cv2 :id=arm-configuration-i2cv2
|
||||
|
||||
* STM32F0xx
|
||||
* STM32F3xx
|
||||
@ -105,9 +105,9 @@ See [this page](https://www.playembedded.org/blog/stm32-i2c-chibios/#8_I2Cv2_I2C
|
||||
|`I2C1_TIMINGR_SCLH` |`38U` |
|
||||
|`I2C1_TIMINGR_SCLL` |`129U` |
|
||||
|
||||
## Functions :id=functions
|
||||
## API :id=api
|
||||
|
||||
### `void i2c_init(void)`
|
||||
### `void i2c_init(void)` :id=api-i2c-init
|
||||
|
||||
Initialize the I2C driver. This function must be called only once, before any of the below functions can be called.
|
||||
|
||||
@ -126,28 +126,28 @@ void i2c_init(void) {
|
||||
|
||||
---
|
||||
|
||||
### `i2c_status_t i2c_start(uint8_t address, uint16_t timeout)`
|
||||
### `i2c_status_t i2c_start(uint8_t address, uint16_t timeout)` :id=api-i2c-start
|
||||
|
||||
Start an I2C transaction.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-i2c-start-arguments
|
||||
|
||||
- `uint8_t address`
|
||||
The 7-bit I2C address of the device (ie. without the read/write bit - this will be set automatically).
|
||||
- `uint16_t timeout`
|
||||
The time in milliseconds to wait for a response from the target device.
|
||||
|
||||
#### Return Value
|
||||
#### Return Value :id=api-i2c-start-return
|
||||
|
||||
`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
|
||||
|
||||
---
|
||||
|
||||
### `i2c_status_t i2c_transmit(uint8_t address, uint8_t *data, uint16_t length, uint16_t timeout)`
|
||||
### `i2c_status_t i2c_transmit(uint8_t address, uint8_t *data, uint16_t length, uint16_t timeout)` :id=api-i2c-transmit
|
||||
|
||||
Send multiple bytes to the selected I2C device.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-i2c-transmit-arguments
|
||||
|
||||
- `uint8_t address`
|
||||
The 7-bit I2C address of the device.
|
||||
@ -158,17 +158,17 @@ Send multiple bytes to the selected I2C device.
|
||||
- `uint16_t timeout`
|
||||
The time in milliseconds to wait for a response from the target device.
|
||||
|
||||
#### Return Value
|
||||
#### Return Value :id=api-i2c-transmit-return
|
||||
|
||||
`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
|
||||
|
||||
---
|
||||
|
||||
### `i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout)`
|
||||
### `i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout)` :id=api-i2c-receive
|
||||
|
||||
Receive multiple bytes from the selected I2C device.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-i2c-receive-arguments
|
||||
|
||||
- `uint8_t address`
|
||||
The 7-bit I2C address of the device.
|
||||
@ -179,17 +179,17 @@ Receive multiple bytes from the selected I2C device.
|
||||
- `uint16_t timeout`
|
||||
The time in milliseconds to wait for a response from the target device.
|
||||
|
||||
#### Return Value
|
||||
#### Return Value :id=api-i2c-receive-return
|
||||
|
||||
`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
|
||||
|
||||
---
|
||||
|
||||
### `i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)`
|
||||
### `i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` :id=api-i2c-writereg
|
||||
|
||||
Writes to a register with an 8-bit address on the I2C device.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-i2c-writereg-arguments
|
||||
|
||||
- `uint8_t devaddr`
|
||||
The 7-bit I2C address of the device.
|
||||
@ -202,17 +202,17 @@ Writes to a register with an 8-bit address on the I2C device.
|
||||
- `uint16_t timeout`
|
||||
The time in milliseconds to wait for a response from the target device.
|
||||
|
||||
#### Return Value
|
||||
#### Return Value :id=api-i2c-writereg-return
|
||||
|
||||
`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
|
||||
|
||||
---
|
||||
|
||||
### `i2c_status_t i2c_writeReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)`
|
||||
### `i2c_status_t i2c_writeReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` :id=api-i2c-writereg16
|
||||
|
||||
Writes to a register with a 16-bit address (big endian) on the I2C device.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-i2c-writereg16-arguments
|
||||
|
||||
- `uint8_t devaddr`
|
||||
The 7-bit I2C address of the device.
|
||||
@ -225,17 +225,17 @@ Writes to a register with a 16-bit address (big endian) on the I2C device.
|
||||
- `uint16_t timeout`
|
||||
The time in milliseconds to wait for a response from the target device.
|
||||
|
||||
#### Return Value
|
||||
#### Return Value :id=api-i2c-writereg16-return
|
||||
|
||||
`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
|
||||
|
||||
---
|
||||
|
||||
### `i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)`
|
||||
### `i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` :id=api-i2c-readreg
|
||||
|
||||
Reads from a register with an 8-bit address on the I2C device.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-i2c-readreg-arguments
|
||||
|
||||
- `uint8_t devaddr`
|
||||
The 7-bit I2C address of the device.
|
||||
@ -246,7 +246,7 @@ Reads from a register with an 8-bit address on the I2C device.
|
||||
- `uint16_t timeout`
|
||||
The time in milliseconds to wait for a response from the target device.
|
||||
|
||||
#### Return Value
|
||||
#### Return Value :id=api-i2c-readreg-return
|
||||
|
||||
`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
|
||||
|
||||
@ -256,7 +256,7 @@ Reads from a register with an 8-bit address on the I2C device.
|
||||
|
||||
Reads from a register with a 16-bit address (big endian) on the I2C device.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-i2c-readreg16-arguments
|
||||
|
||||
- `uint8_t devaddr`
|
||||
The 7-bit I2C address of the device.
|
||||
@ -267,12 +267,12 @@ Reads from a register with a 16-bit address (big endian) on the I2C device.
|
||||
- `uint16_t timeout`
|
||||
The time in milliseconds to wait for a response from the target device.
|
||||
|
||||
#### Return Value
|
||||
#### Return Value :id=api-i2c-readreg16-return
|
||||
|
||||
`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
|
||||
|
||||
---
|
||||
|
||||
### `i2c_status_t i2c_stop(void)`
|
||||
### `i2c_status_t i2c_stop(void)` :id=api-i2c-stop
|
||||
|
||||
Stop the current I2C transaction.
|
||||
|
@ -109,8 +109,8 @@ QMK が完全な `info.json` を生成するときはいつでも、`config.h`
|
||||
パズルの最後のピースは、ビルドシステムに新しいオプションを提供することです。
|
||||
これは、2つのファイルを生成することによって行われます。
|
||||
|
||||
* `.build/obj_<keyboard>/src/info_config.h`
|
||||
* `.build/obj_<keyboard>/src/rules.mk`
|
||||
* `.build/obj_<keyboard>_<keymap>/src/info_config.h`
|
||||
* `.build/obj_<keyboard>_<keymap>/src/rules.mk`
|
||||
|
||||
この2つのファイルは、次のコードによって生成されます。
|
||||
|
||||
|
@ -848,8 +848,8 @@ See also: [Unicode Support](feature_unicode.md)
|
||||
|Key |Aliases |Description |
|
||||
|----------------------------|---------|----------------------------------------------------------------|
|
||||
|`UC(c)` | |Send Unicode code point `c`, up to `0x7FFF` |
|
||||
|`X(i)` | |Send Unicode code point at index `i` in `unicode_map` |
|
||||
|`XP(i, j)` | |Send Unicode code point at index `i`, or `j` if Shift/Caps is on|
|
||||
|`UM(i)` | |Send Unicode code point at index `i` in `unicode_map` |
|
||||
|`UP(i, j)` | |Send Unicode code point at index `i`, or `j` if Shift/Caps is on|
|
||||
|`QK_UNICODE_MODE_NEXT` |`UC_NEXT`|Cycle through selected input modes |
|
||||
|`QK_UNICODE_MODE_PREVIOUS` |`UC_PREV`|Cycle through selected input modes in reverse |
|
||||
|`QK_UNICODE_MODE_MACOS` |`UC_MAC` |Switch to macOS input |
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
The [QMK Configurator](https://config.qmk.fm) is an online graphical user interface that generates QMK Firmware `.hex` or `.bin` files.
|
||||
|
||||
It should be noted that Configurator cannot produce firmwares for keyboards using a different controller than they were designed for, i.e. an RP2040 controller on a board designed for pro micro. You will have to use the command line [converters](https://docs.qmk.fm/#/feature_converters?id=supported-converters) for this.
|
||||
|
||||
Watch the [Video Tutorial](https://www.youtube.com/watch?v=-imgglzDMdY). Many people find that is enough information to start programming their own keyboard.
|
||||
|
||||
The QMK Configurator works best with Chrome or Firefox.
|
||||
|
@ -85,6 +85,8 @@ Visit the [QMK Configurator](https://config.qmk.fm/#/) to create a keymap file:
|
||||
3. Customise the key layout according to your preference.
|
||||
4. Select download next to **KEYMAP.JSON** and save the JSON file into the `~/qmk_keymap/` folder.
|
||||
|
||||
!> **Important:** Make sure that the GitHub username you use in step 2 is correct. If it is not, the build process will fail to locate your files in the right folder.
|
||||
|
||||
### Add a GitHub Action workflow
|
||||
|
||||
Open the file `~/qmk_keymap/.github/workflows/build.yml` with your favorite [text editor](newbs_learn_more_resources.md#text-editor-resources), paste the following workflow content, and save it:
|
||||
|
@ -15,7 +15,7 @@ Different keyboards have different ways to enter this special mode. If your PCB
|
||||
* Press the physical `RESET` button, usually located on the underside of the PCB
|
||||
* Locate header pins on the PCB labeled `RESET` and `GND`, and short them together while plugging your PCB in
|
||||
|
||||
If you've attempted all of the above to no avail, and the main chip on the board says `STM32` on it, this may be a bit more complicated. Generally your best bet is to ask on [Discord](https://discord.gg/Uq7gcHh) for assistance. It's likely some photos of the board will be asked for -- if you can get them ready beforehand it'll help move things along!
|
||||
If you've attempted all of the above to no avail, and the main chip on the board says `STM32` or `RP2-B1` on it, this may be a bit more complicated. Generally your best bet is to ask on [Discord](https://discord.gg/Uq7gcHh) for assistance. It's likely some photos of the board will be asked for -- if you can get them ready beforehand it'll help move things along!
|
||||
|
||||
Otherwise, you should see a message in yellow, similar to this in QMK Toolbox:
|
||||
|
||||
@ -31,6 +31,8 @@ The simplest way to flash your keyboard will be with the [QMK Toolbox](https://g
|
||||
|
||||
However, the Toolbox is currently only available for Windows and macOS. If you're using Linux (or just wish to flash the firmware from the command line), skip to the [Flash your Keyboard from the Command Line](#flash-your-keyboard-from-the-command-line) section.
|
||||
|
||||
?> QMK Toolbox is not necessary for flashing [RP2040 devices](https://docs.qmk.fm/#/flashing?id=raspberry-pi-rp2040-uf2).
|
||||
|
||||
### Load the File into QMK Toolbox
|
||||
|
||||
Begin by opening the QMK Toolbox application. You'll want to locate the firmware file in Finder or Explorer. Your keyboard firmware may be in one of two formats- `.hex` or `.bin`. QMK tries to copy the appropriate one for your keyboard into the root `qmk_firmware` directory.
|
||||
|
@ -120,7 +120,7 @@ NOTE: remember to follow the instructions printed at the end of installation (us
|
||||
|
||||
### ** Windows **
|
||||
|
||||
After installing QMK you can set it up with this command:
|
||||
Open QMK MSYS and run the following command:
|
||||
|
||||
qmk setup
|
||||
|
||||
@ -128,7 +128,7 @@ In most situations you will want to answer `y` to all of the prompts.
|
||||
|
||||
### ** macOS **
|
||||
|
||||
After installing QMK you can set it up with this command:
|
||||
Open Terminal and run the following command:
|
||||
|
||||
qmk setup
|
||||
|
||||
@ -136,7 +136,7 @@ In most situations you will want to answer `y` to all of the prompts.
|
||||
|
||||
### ** Linux/WSL **
|
||||
|
||||
After installing QMK you can set it up with this command:
|
||||
Open your preferred terminal app and run the following command:
|
||||
|
||||
qmk setup
|
||||
|
||||
@ -150,7 +150,7 @@ Luckily, the fix is easy. Run this as your user: `echo 'PATH="$HOME/.local/bin:$
|
||||
|
||||
### ** FreeBSD **
|
||||
|
||||
After installing QMK you can set it up with this command:
|
||||
Open your preferred terminal app and run the following command:
|
||||
|
||||
qmk setup
|
||||
|
||||
|
@ -96,7 +96,7 @@ There are a number of extensions that you may want to install:
|
||||
* [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.
|
||||
* [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) - _[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.
|
||||
|
||||
|
@ -11,7 +11,6 @@ If there are any inconsistencies with these recommendations, you're best off [cr
|
||||
- if submitter _does_ use their own `master` branch, they'll be given a link to the ["how to git"](newbs_git_using_your_master_branch.md) page after merging -- (end of this document will contain the contents of the message)
|
||||
- PRs should contain the smallest amount of modifications required for a single change to the codebase
|
||||
- multiple keyboards at the same time is not acceptable
|
||||
- exception: keymaps for a single user targeting multiple keyboards and/or userspace is acceptable
|
||||
- **the smaller the PR, the higher likelihood of a quicker review, higher likelihood of quicker merge, and less chance of conflicts**
|
||||
- newly-added directories and filenames must be lowercase
|
||||
- the lowercase requirement may be relaxed if upstream sources originally had uppercase characters (e.g. LUFA, ChibiOS, or imported files from other repositories etc.)
|
||||
@ -40,9 +39,11 @@ If there are any inconsistencies with these recommendations, you're best off [cr
|
||||
|
||||
## Keymap PRs
|
||||
|
||||
Note that personal keymap submissions will no longer be accepted. This section applies to manufacturer-supported keymaps.
|
||||
|
||||
- `#include QMK_KEYBOARD_H` preferred to including specific board files
|
||||
- prefer layer `enum`s to `#define`s
|
||||
- require custom keycode `enum`s to `#define`s, first entry must have ` = SAFE_RANGE`
|
||||
- custom keycode `enum`s must have first entry `= SAFE_RANGE`
|
||||
- terminating backslash (`\`) in lines of LAYOUT macro parameters is superfluous and should be removed
|
||||
- some care with spacing (e.g., alignment on commas or first char of keycodes) makes for a much nicer-looking keymap
|
||||
|
||||
@ -132,6 +133,7 @@ https://github.com/qmk/qmk_firmware/pulls?q=is%3Apr+is%3Aclosed+label%3Akeyboard
|
||||
- if using `MO(1)` and `MO(2)` keycodes together to access a third layer, the [Tri Layer](https://docs.qmk.fm/#/feature_tri_layer) feature should be used, rather than manually implementing this using `layer_on/off()` and `update_tri_layer()` functions in the keymap's `process_record_user()`.
|
||||
- default (and via) keymaps should be "pristine"
|
||||
- bare minimum to be used as a "clean slate" for another user to develop their own user-specific keymap
|
||||
- what does pristine mean? no custom keycodes. no advanced features like tap dance or macros. basic mod taps and home row mods would be acceptable where their use is necessary
|
||||
- standard layouts preferred in these keymaps, if possible
|
||||
- should use [encoder map feature](https://docs.qmk.fm/#/feature_encoders?id=encoder-map), rather than `encoder_update_user()`
|
||||
- default keymap should not enable VIA -- the VIA integration documentation requires a keymap called `via`
|
||||
|
@ -2,11 +2,15 @@
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
.markdown-section p.tip,
|
||||
.markdown-section tr:nth-child(2n) {
|
||||
background-color:#444;
|
||||
}
|
||||
|
||||
.markdown-section p.tip {
|
||||
background-color:#555;
|
||||
color:#FFF;
|
||||
}
|
||||
|
||||
.markdown-section tr {
|
||||
border-top: 1px solid #555;
|
||||
}
|
||||
@ -16,7 +20,7 @@
|
||||
}
|
||||
|
||||
.markdown-section p.tip code {
|
||||
background-color: #555;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
@ -15,3 +15,5 @@ On this page we have documented keycodes between `0x00FF` and `0xFFFF` which are
|
||||
|`QK_CLEAR_EEPROM`|`EE_CLR` |Reinitializes the keyboard's EEPROM (persistent memory) |
|
||||
|`QK_MAKE` | |Sends `qmk compile -kb (keyboard) -km (keymap)`, or `qmk flash` if shift is held. Puts keyboard into bootloader mode if shift & control are held |
|
||||
|`QK_REBOOT` |`QK_RBT` |Resets the keyboard. Does not load the bootloader |
|
||||
|
||||
!> Note: `QK_MAKE` requires `#define ENABLE_COMPILE_KEYCODE` in your config.h to function.
|
||||
|
@ -40,7 +40,7 @@ Supported devices:
|
||||
| `QUANTUM_PAINTER_NUM_FONTS` | `4` | The maximum number of fonts that can be loaded at any one time. |
|
||||
| `QUANTUM_PAINTER_CONCURRENT_ANIMATIONS` | `4` | The maximum number of animations that can be executed at the same 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_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_PIXDATA_BUFFER_SIZE` | `1024` | 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_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. |
|
||||
|
@ -113,7 +113,7 @@ A modifier that acts as if it is held down until another key is released, so you
|
||||
A low cost AVR development board. Clones of this device are often found on ebay very inexpensively (under $5) but people often struggle with flashing their pro micros.
|
||||
|
||||
## Pull Request
|
||||
A request to submit code to QMK. We encourage all users to submit Pull Requests for their personal keymaps.
|
||||
A request to submit code to QMK. We encourage all users to submit Pull Requests for bugfixes and new features.
|
||||
|
||||
## QWERTY
|
||||
The standard English keyboard layout, and often a shortcut for other language's standard layouts. Named for the first 6 letters on the keyboard.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,8 @@
|
||||
# SPI Master Driver
|
||||
# SPI Master Driver :id=spi-master-driver
|
||||
|
||||
The SPI Master drivers used in QMK have a set of common functions to allow portability between MCUs.
|
||||
|
||||
## AVR Configuration
|
||||
## AVR Configuration :id=avr-configuration
|
||||
|
||||
No special setup is required - just connect the `SS`, `SCK`, `MOSI` and `MISO` pins of your SPI devices to the matching pins on the MCU:
|
||||
|
||||
@ -16,7 +16,7 @@ No special setup is required - just connect the `SS`, `SCK`, `MOSI` and `MISO` p
|
||||
You may use more than one slave select pin, not just the `SS` pin. This is useful when you have multiple devices connected and need to communicate with them individually.
|
||||
`SPI_SS_PIN` can be passed to `spi_start()` to refer to `SS`.
|
||||
|
||||
## ChibiOS/ARM Configuration
|
||||
## ChibiOS/ARM Configuration :id=arm-configuration
|
||||
|
||||
You'll need to determine which pins can be used for SPI -- as an example, STM32 parts generally have multiple SPI peripherals, labeled SPI1, SPI2, SPI3 etc.
|
||||
|
||||
@ -49,19 +49,19 @@ Configuration-wise, you'll need to set up the peripheral as per your MCU's datas
|
||||
|
||||
As per the AVR configuration, you may choose any other standard GPIO as a slave select pin, which should be supplied to `spi_start()`.
|
||||
|
||||
## Functions
|
||||
## API :id=api
|
||||
|
||||
### `void spi_init(void)`
|
||||
### `void spi_init(void)` :id=api-spi-init
|
||||
|
||||
Initialize the SPI driver. This function must be called only once, before any of the below functions can be called.
|
||||
|
||||
---
|
||||
|
||||
### `bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor)`
|
||||
### `bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor)` :id=api-spi-start
|
||||
|
||||
Start an SPI transaction.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-spi-start-arguments
|
||||
|
||||
- `pin_t slavePin`
|
||||
The QMK pin to assert as the slave select pin, eg. `B4`.
|
||||
@ -80,71 +80,71 @@ Start an SPI transaction.
|
||||
- `uint16_t divisor`
|
||||
The SPI clock divisor, will be rounded up to the nearest power of two. This number can be calculated by dividing the MCU's clock speed by the desired SPI clock speed. For example, an MCU running at 8 MHz wanting to talk to an SPI device at 4 MHz would set the divisor to `2`.
|
||||
|
||||
#### Return Value
|
||||
#### Return Value :id=api-spi-start-return
|
||||
|
||||
`false` if the supplied parameters are invalid or the SPI peripheral is already in use, or `true`.
|
||||
|
||||
---
|
||||
|
||||
### `spi_status_t spi_write(uint8_t data)`
|
||||
### `spi_status_t spi_write(uint8_t data)` :id=api-spi-write
|
||||
|
||||
Write a byte to the selected SPI device.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-spi-write-arguments
|
||||
|
||||
- `uint8_t data`
|
||||
The byte to write.
|
||||
|
||||
#### Return Value
|
||||
#### Return Value :id=api-spi-write-return
|
||||
|
||||
`SPI_STATUS_TIMEOUT` if the timeout period elapses, or `SPI_STATUS_SUCCESS`.
|
||||
|
||||
---
|
||||
|
||||
### `spi_status_t spi_read(void)`
|
||||
### `spi_status_t spi_read(void)` :id=api-spi-read
|
||||
|
||||
Read a byte from the selected SPI device.
|
||||
|
||||
#### Return Value
|
||||
#### Return Value :id=api-spi-read-return
|
||||
|
||||
`SPI_STATUS_TIMEOUT` if the timeout period elapses, or the byte read from the device.
|
||||
|
||||
---
|
||||
|
||||
### `spi_status_t spi_transmit(const uint8_t *data, uint16_t length)`
|
||||
### `spi_status_t spi_transmit(const uint8_t *data, uint16_t length)` :id=api-spi-transmit
|
||||
|
||||
Send multiple bytes to the selected SPI device.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-spi-transmit-arguments
|
||||
|
||||
- `const uint8_t *data`
|
||||
A pointer to the data to write from.
|
||||
- `uint16_t length`
|
||||
The number of bytes to write. Take care not to overrun the length of `data`.
|
||||
|
||||
#### Return Value
|
||||
#### Return Value :id=api-spi-transmit-return
|
||||
|
||||
`SPI_STATUS_TIMEOUT` if the timeout period elapses, `SPI_STATUS_ERROR` if some other error occurs, otherwise `SPI_STATUS_SUCCESS`.
|
||||
|
||||
---
|
||||
|
||||
### `spi_status_t spi_receive(uint8_t *data, uint16_t length)`
|
||||
### `spi_status_t spi_receive(uint8_t *data, uint16_t length)` :id=api-spi-receive
|
||||
|
||||
Receive multiple bytes from the selected SPI device.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-spi-receive-arguments
|
||||
|
||||
- `uint8_t *data`
|
||||
A pointer to the buffer to read into.
|
||||
- `uint16_t length`
|
||||
The number of bytes to read. Take care not to overrun the length of `data`.
|
||||
|
||||
#### Return Value
|
||||
#### Return Value :id=api-spi-receive-return
|
||||
|
||||
`SPI_STATUS_TIMEOUT` if the timeout period elapses, `SPI_STATUS_ERROR` if some other error occurs, otherwise `SPI_STATUS_SUCCESS`.
|
||||
|
||||
---
|
||||
|
||||
### `void spi_stop(void)`
|
||||
### `void spi_stop(void)` :id=api-spi-stop
|
||||
|
||||
End the current SPI transaction. This will deassert the slave select pin and reset the endianness, mode and divisor configured by `spi_start()`.
|
||||
|
@ -27,8 +27,7 @@ SPACE_CADET_ENABLE = no
|
||||
GRAVE_ESC_ENABLE = no
|
||||
MAGIC_ENABLE = no
|
||||
```
|
||||
These features are enabled by default, but may not be needed. Double check to make sure, though.
|
||||
Largest in size is "magic" -- the QMK magic keycodes -- which control things like NKRO toggling, GUI and ALT/CTRL swapping, etc. Disabling it will disable those functions.
|
||||
These features are enabled by default, but they may not be needed. Double check to make sure. The [Magic Keycodes](keycodes_magic.md) are the largest and control things like NKRO toggling, GUI and ALT/CTRL swapping, etc. Disabling them will disable those functions. See [Magic Functions](#magic-functions) for disabling related functions.
|
||||
|
||||
If you use `sprintf` or `snprintf` functions you can save around ~400 Bytes by enabling this option.
|
||||
```make
|
||||
@ -75,7 +74,7 @@ MUSIC_ENABLE = no
|
||||
|
||||
There are also some options for layers, that can reduce the firmware size. All of these settings are for your `config.h`.
|
||||
|
||||
You can limit the number of layers that the firmware uses -- if you're using less than 8 layers in total:
|
||||
You can limit the number of layers that the firmware uses -- if you're using up to 8 layers in total:
|
||||
```c
|
||||
#define LAYER_STATE_8BIT
|
||||
```
|
||||
@ -88,6 +87,21 @@ Or if you're not using layers at all, you can outright remove the functionality
|
||||
#define NO_ACTION_LAYER
|
||||
```
|
||||
|
||||
## Magic Functions
|
||||
|
||||
There are two `__attribute__ ((weak))` placeholder functions available to customize magic keycodes. If you are not using that feature to swap keycodes, such as backslash with backspace, add the following to your `keymap.c` or user space code:
|
||||
```c
|
||||
uint16_t keycode_config(uint16_t keycode) {
|
||||
return keycode;
|
||||
}
|
||||
```
|
||||
Likewise, if you are not using magic keycodes to swap modifiers, such as Control with GUI, add the following to your `keymap.c` or user space code:
|
||||
```c
|
||||
uint8_t mod_config(uint8_t mod) {
|
||||
return mod;
|
||||
}
|
||||
```
|
||||
Both of them will overwrite the placeholder functions with a simple return statement to reduce firmware size.
|
||||
|
||||
## OLED tweaks
|
||||
|
||||
|
@ -460,6 +460,31 @@ bool get_retro_tapping(uint16_t keycode, keyrecord_t *record) {
|
||||
}
|
||||
```
|
||||
|
||||
If the programs you use bind an action to taps of modifier keys (e.g. tapping left GUI to bring up the applications menu or tapping left Alt to focus the menu bar), you may find that using retro-tapping falsely triggers those actions. To counteract this, you can define a `DUMMY_MOD_NEUTRALIZER_KEYCODE` in `config.h` that will get sent in between the register and unregister events of a held mod-tap key. That way, the programs on your computer will no longer interpret the mod suppression induced by retro-tapping as a lone tap of a modifier key and will thus not falsely trigger the undesired action.
|
||||
|
||||
Naturally, for this technique to be effective, you must choose a `DUMMY_MOD_NEUTRALIZER_KEYCODE` for which no keyboard shortcuts are bound to. Recommended values are: `KC_RIGHT_CTRL` or `KC_F18`.
|
||||
Please note that `DUMMY_MOD_NEUTRALIZER_KEYCODE` must be a basic, unmodified, HID keycode so values like `KC_NO`, `KC_TRANSPARENT` or `KC_PIPE` aka `S(KC_BACKSLASH)` are not permitted.
|
||||
|
||||
By default, only left Alt and left GUI are neutralized. If you want to change the list of applicable modifier masks, use the following in your `config.h`:
|
||||
|
||||
```c
|
||||
#define MODS_TO_NEUTRALIZE { <mod_mask_1>, <mod_mask_2>, ... }
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
```c
|
||||
#define DUMMY_MOD_NEUTRALIZER_KEYCODE KC_RIGHT_CTRL
|
||||
|
||||
// Neutralize left alt and left GUI (Default value)
|
||||
#define MODS_TO_NEUTRALIZE { MOD_BIT(KC_LEFT_ALT), MOD_BIT(KC_LEFT_GUI) }
|
||||
|
||||
// Neutralize left alt, left GUI, right GUI and left Control+Shift
|
||||
#define MODS_TO_NEUTRALIZE { MOD_BIT(KC_LEFT_ALT), MOD_BIT(KC_LEFT_GUI), MOD_BIT(KC_RIGHT_GUI), MOD_BIT(KC_LEFT_CTRL)|MOD_BIT(KC_LEFT_SHIFT) }
|
||||
```
|
||||
|
||||
!> Do not use `MOD_xxx` constants like `MOD_LSFT` or `MOD_RALT`, since they're 5-bit packed bit-arrays while `MODS_TO_NEUTRALIZE` expects a list of 8-bit packed bit-arrays. Use `MOD_BIT(<kc>)` or `MOD_MASK_xxx` instead.
|
||||
|
||||
### Retro Shift
|
||||
|
||||
[Auto Shift,](feature_auto_shift.md) has its own version of `retro tapping` called `retro shift`. It is extremely similar to `retro tapping`, but holding the key past `AUTO_SHIFT_TIMEOUT` results in the value it sends being shifted. Other configurations also affect it differently; see [here](feature_auto_shift.md#retro-shift) for more information.
|
||||
|
@ -1,10 +1,10 @@
|
||||
# UART Driver
|
||||
# UART Driver :id=uart-driver
|
||||
|
||||
The UART drivers used in QMK have a set of common functions to allow portability between MCUs.
|
||||
|
||||
Currently, this driver does not support enabling hardware flow control (the `RTS` and `CTS` pins) if available, but may do so in future.
|
||||
|
||||
## AVR Configuration
|
||||
## AVR Configuration :id=avr-configuration
|
||||
|
||||
No special setup is required - just connect the `RX` and `TX` pins of your UART device to the opposite pins on the MCU:
|
||||
|
||||
@ -16,7 +16,7 @@ No special setup is required - just connect the `RX` and `TX` pins of your UART
|
||||
|ATmega32A |`D1`|`D0`|*n/a*|*n/a*|
|
||||
|ATmega328/P |`D1`|`D0`|*n/a*|*n/a*|
|
||||
|
||||
## ChibiOS/ARM Configuration
|
||||
## ChibiOS/ARM Configuration :id=arm-configuration
|
||||
|
||||
You'll need to determine which pins can be used for UART -- as an example, STM32 parts generally have multiple UART peripherals, labeled USART1, USART2, USART3 etc.
|
||||
|
||||
@ -47,45 +47,45 @@ Configuration-wise, you'll need to set up the peripheral as per your MCU's datas
|
||||
|`#define SD1_RTS_PIN` |The pin to use for RTS |`A12` |
|
||||
|`#define SD1_RTS_PAL_MODE`|The alternate function mode for RTS |`7` |
|
||||
|
||||
## Functions
|
||||
## API :id=api
|
||||
|
||||
### `void uart_init(uint32_t baud)`
|
||||
### `void uart_init(uint32_t baud)` :id=api-uart-init
|
||||
|
||||
Initialize the UART driver. This function must be called only once, before any of the below functions can be called.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-uart-init-arguments
|
||||
|
||||
- `uint32_t baud`
|
||||
The baud rate to transmit and receive at. This may depend on the device you are communicating with. Common values are 1200, 2400, 4800, 9600, 19200, 38400, 57600, and 115200.
|
||||
|
||||
---
|
||||
|
||||
### `void uart_write(uint8_t data)`
|
||||
### `void uart_write(uint8_t data)` :id=api-uart-write
|
||||
|
||||
Transmit a single byte.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-uart-write-arguments
|
||||
|
||||
- `uint8_t data`
|
||||
The byte to write.
|
||||
|
||||
---
|
||||
|
||||
### `uint8_t uart_read(void)`
|
||||
### `uint8_t uart_read(void)` :id=api-uart-read
|
||||
|
||||
Receive a single byte.
|
||||
|
||||
#### Return Value
|
||||
#### Return Value :id=api-uart-read-return
|
||||
|
||||
The byte read from the receive buffer. This function will block if the buffer is empty (ie. no data to read).
|
||||
|
||||
---
|
||||
|
||||
### `void uart_transmit(const uint8_t *data, uint16_t length)`
|
||||
### `void uart_transmit(const uint8_t *data, uint16_t length)` :id=api-uart-transmit
|
||||
|
||||
Transmit multiple bytes.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-uart-transmit-arguments
|
||||
|
||||
- `const uint8_t *data`
|
||||
A pointer to the data to write from.
|
||||
@ -94,11 +94,11 @@ Transmit multiple bytes.
|
||||
|
||||
---
|
||||
|
||||
### `void uart_receive(char *data, uint16_t length)`
|
||||
### `void uart_receive(char *data, uint16_t length)` :id=api-uart-receive
|
||||
|
||||
Receive multiple bytes.
|
||||
|
||||
#### Arguments
|
||||
#### Arguments :id=api-uart-receive-arguments
|
||||
|
||||
- `uint8_t *data`
|
||||
A pointer to the buffer to read into.
|
||||
@ -107,10 +107,10 @@ Receive multiple bytes.
|
||||
|
||||
---
|
||||
|
||||
### `bool uart_available(void)`
|
||||
### `bool uart_available(void)` :id=api-uart-available
|
||||
|
||||
Return whether the receive buffer contains data. Call this function to determine if `uart_read()` will return data immediately.
|
||||
|
||||
#### Return Value
|
||||
#### Return Value :id=api-uart-available-return
|
||||
|
||||
`true` if the receive buffer length is non-zero.
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "quantum.h"
|
||||
#include "backlight.h"
|
||||
#include "backlight_driver_common.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifdef BACKLIGHT_BREATHING
|
||||
# error "Backlight breathing is not available for software PWM. Please disable."
|
@ -1,122 +0,0 @@
|
||||
/* Copyright 2018 ishtob
|
||||
* Driver for DRV2605L written for QMK
|
||||
*
|
||||
* 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 "DRV2605L.h"
|
||||
#include "print.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
uint8_t DRV2605L_transfer_buffer[2];
|
||||
uint8_t DRV2605L_read_register;
|
||||
|
||||
void DRV_write(uint8_t drv_register, uint8_t settings) {
|
||||
DRV2605L_transfer_buffer[0] = drv_register;
|
||||
DRV2605L_transfer_buffer[1] = settings;
|
||||
i2c_transmit(DRV2605L_BASE_ADDRESS << 1, DRV2605L_transfer_buffer, 2, 100);
|
||||
}
|
||||
|
||||
uint8_t DRV_read(uint8_t regaddress) {
|
||||
i2c_readReg(DRV2605L_BASE_ADDRESS << 1, regaddress, &DRV2605L_read_register, 1, 100);
|
||||
|
||||
return DRV2605L_read_register;
|
||||
}
|
||||
|
||||
void DRV_init(void) {
|
||||
i2c_init();
|
||||
/* 0x07 sets DRV2605 into calibration mode */
|
||||
DRV_write(DRV_MODE, 0x07);
|
||||
|
||||
// DRV_write(DRV_FEEDBACK_CTRL,0xB6);
|
||||
|
||||
#if FB_ERM_LRA == 0
|
||||
/* ERM settings */
|
||||
DRV_write(DRV_RATED_VOLT, (RATED_VOLTAGE / 21.33) * 1000);
|
||||
# if ERM_OPEN_LOOP == 0
|
||||
DRV_write(DRV_OVERDRIVE_CLAMP_VOLT, (((V_PEAK * (DRIVE_TIME + BLANKING_TIME + IDISS_TIME)) / 0.02133) / (DRIVE_TIME - 0.0003)));
|
||||
# elif ERM_OPEN_LOOP == 1
|
||||
DRV_write(DRV_OVERDRIVE_CLAMP_VOLT, (V_PEAK / 0.02196));
|
||||
# endif
|
||||
#elif FB_ERM_LRA == 1
|
||||
DRV_write(DRV_RATED_VOLT, ((V_RMS * sqrt(1 - ((4 * ((150 + (SAMPLE_TIME * 50)) * 0.000001)) + 0.0003) * F_LRA) / 0.02071)));
|
||||
# if LRA_OPEN_LOOP == 0
|
||||
DRV_write(DRV_OVERDRIVE_CLAMP_VOLT, ((V_PEAK / sqrt(1 - (F_LRA * 0.0008)) / 0.02133)));
|
||||
# elif LRA_OPEN_LOOP == 1
|
||||
DRV_write(DRV_OVERDRIVE_CLAMP_VOLT, (V_PEAK / 0.02196));
|
||||
# endif
|
||||
#endif
|
||||
|
||||
DRVREG_FBR FB_SET;
|
||||
FB_SET.Bits.ERM_LRA = FB_ERM_LRA;
|
||||
FB_SET.Bits.BRAKE_FACTOR = FB_BRAKEFACTOR;
|
||||
FB_SET.Bits.LOOP_GAIN = FB_LOOPGAIN;
|
||||
FB_SET.Bits.BEMF_GAIN = 0; /* auto-calibration populates this field*/
|
||||
DRV_write(DRV_FEEDBACK_CTRL, (uint8_t)FB_SET.Byte);
|
||||
DRVREG_CTRL1 C1_SET;
|
||||
C1_SET.Bits.C1_DRIVE_TIME = DRIVE_TIME;
|
||||
C1_SET.Bits.C1_AC_COUPLE = AC_COUPLE;
|
||||
C1_SET.Bits.C1_STARTUP_BOOST = STARTUP_BOOST;
|
||||
DRV_write(DRV_CTRL_1, (uint8_t)C1_SET.Byte);
|
||||
DRVREG_CTRL2 C2_SET;
|
||||
C2_SET.Bits.C2_BIDIR_INPUT = BIDIR_INPUT;
|
||||
C2_SET.Bits.C2_BRAKE_STAB = BRAKE_STAB;
|
||||
C2_SET.Bits.C2_SAMPLE_TIME = SAMPLE_TIME;
|
||||
C2_SET.Bits.C2_BLANKING_TIME = BLANKING_TIME;
|
||||
C2_SET.Bits.C2_IDISS_TIME = IDISS_TIME;
|
||||
DRV_write(DRV_CTRL_2, (uint8_t)C2_SET.Byte);
|
||||
DRVREG_CTRL3 C3_SET;
|
||||
C3_SET.Bits.C3_LRA_OPEN_LOOP = LRA_OPEN_LOOP;
|
||||
C3_SET.Bits.C3_N_PWM_ANALOG = N_PWM_ANALOG;
|
||||
C3_SET.Bits.C3_LRA_DRIVE_MODE = LRA_DRIVE_MODE;
|
||||
C3_SET.Bits.C3_DATA_FORMAT_RTO = DATA_FORMAT_RTO;
|
||||
C3_SET.Bits.C3_SUPPLY_COMP_DIS = SUPPLY_COMP_DIS;
|
||||
C3_SET.Bits.C3_ERM_OPEN_LOOP = ERM_OPEN_LOOP;
|
||||
C3_SET.Bits.C3_NG_THRESH = NG_THRESH;
|
||||
DRV_write(DRV_CTRL_3, (uint8_t)C3_SET.Byte);
|
||||
DRVREG_CTRL4 C4_SET;
|
||||
C4_SET.Bits.C4_ZC_DET_TIME = ZC_DET_TIME;
|
||||
C4_SET.Bits.C4_AUTO_CAL_TIME = AUTO_CAL_TIME;
|
||||
DRV_write(DRV_CTRL_4, (uint8_t)C4_SET.Byte);
|
||||
DRV_write(DRV_LIB_SELECTION, LIB_SELECTION);
|
||||
|
||||
DRV_write(DRV_GO, 0x01);
|
||||
|
||||
/* 0x00 sets DRV2605 out of standby and to use internal trigger
|
||||
* 0x01 sets DRV2605 out of standby and to use external trigger */
|
||||
DRV_write(DRV_MODE, 0x00);
|
||||
|
||||
// Play greeting sequence
|
||||
DRV_write(DRV_GO, 0x00);
|
||||
DRV_write(DRV_WAVEFORM_SEQ_1, DRV_GREETING);
|
||||
DRV_write(DRV_GO, 0x01);
|
||||
}
|
||||
|
||||
void DRV_rtp_init(void) {
|
||||
DRV_write(DRV_GO, 0x00);
|
||||
DRV_write(DRV_RTP_INPUT, 20); // 20 is the lowest value I've found where haptics can still be felt.
|
||||
DRV_write(DRV_MODE, 0x05);
|
||||
DRV_write(DRV_GO, 0x01);
|
||||
}
|
||||
|
||||
void DRV_amplitude(uint8_t amplitude) {
|
||||
DRV_write(DRV_RTP_INPUT, amplitude);
|
||||
}
|
||||
|
||||
void DRV_pulse(uint8_t sequence) {
|
||||
DRV_write(DRV_GO, 0x00);
|
||||
DRV_write(DRV_WAVEFORM_SEQ_1, sequence);
|
||||
DRV_write(DRV_GO, 0x01);
|
||||
}
|
@ -1,406 +0,0 @@
|
||||
/* Copyright 2018 ishtob
|
||||
* Driver for DRV2605L written for QMK
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "i2c_master.h"
|
||||
|
||||
/* Initialization settings
|
||||
|
||||
* Feedback Control Settings */
|
||||
#ifndef FB_ERM_LRA
|
||||
# define FB_ERM_LRA 1 /* For ERM:0 or LRA:1*/
|
||||
#endif
|
||||
#ifndef FB_BRAKEFACTOR
|
||||
# define FB_BRAKEFACTOR 3 /* For 1x:0, 2x:1, 3x:2, 4x:3, 6x:4, 8x:5, 16x:6, Disable Braking:7 */
|
||||
#endif
|
||||
#ifndef FB_LOOPGAIN
|
||||
# define FB_LOOPGAIN 1 /* For Low:0, Medium:1, High:2, Very High:3 */
|
||||
#endif
|
||||
|
||||
/* LRA specific settings */
|
||||
#if FB_ERM_LRA == 1
|
||||
# ifndef V_RMS
|
||||
# define V_RMS 2.0
|
||||
# endif
|
||||
# ifndef V_PEAK
|
||||
# define V_PEAK 2.1
|
||||
# endif
|
||||
# ifndef F_LRA
|
||||
# define F_LRA 205
|
||||
# endif
|
||||
# ifndef RATED_VOLTAGE
|
||||
# define RATED_VOLTAGE 2 /* 2v as safe range in case device voltage is not set */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef RATED_VOLTAGE
|
||||
# define RATED_VOLTAGE 2 /* 2v as safe range in case device voltage is not set */
|
||||
#endif
|
||||
#ifndef V_PEAK
|
||||
# define V_PEAK 2.8
|
||||
#endif
|
||||
|
||||
/* Library Selection */
|
||||
#ifndef LIB_SELECTION
|
||||
# if FB_ERM_LRA == 1
|
||||
# define LIB_SELECTION 6 /* For Empty:0' TS2200 library A to D:1-5, LRA Library: 6 */
|
||||
# else
|
||||
# define LIB_SELECTION 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef DRV_GREETING
|
||||
# define DRV_GREETING alert_750ms
|
||||
#endif
|
||||
#ifndef DRV_MODE_DEFAULT
|
||||
# define DRV_MODE_DEFAULT strong_click1
|
||||
#endif
|
||||
|
||||
/* Control 1 register settings */
|
||||
#ifndef DRIVE_TIME
|
||||
# define DRIVE_TIME 25
|
||||
#endif
|
||||
#ifndef AC_COUPLE
|
||||
# define AC_COUPLE 0
|
||||
#endif
|
||||
#ifndef STARTUP_BOOST
|
||||
# define STARTUP_BOOST 1
|
||||
#endif
|
||||
|
||||
/* Control 2 Settings */
|
||||
#ifndef BIDIR_INPUT
|
||||
# define BIDIR_INPUT 1
|
||||
#endif
|
||||
#ifndef BRAKE_STAB
|
||||
# define BRAKE_STAB 1 /* Loopgain is reduced when braking is almost complete to improve stability */
|
||||
#endif
|
||||
#ifndef SAMPLE_TIME
|
||||
# define SAMPLE_TIME 3
|
||||
#endif
|
||||
#ifndef BLANKING_TIME
|
||||
# define BLANKING_TIME 1
|
||||
#endif
|
||||
#ifndef IDISS_TIME
|
||||
# define IDISS_TIME 1
|
||||
#endif
|
||||
|
||||
/* Control 3 settings */
|
||||
#ifndef NG_THRESH
|
||||
# define NG_THRESH 2
|
||||
#endif
|
||||
#ifndef ERM_OPEN_LOOP
|
||||
# define ERM_OPEN_LOOP 1
|
||||
#endif
|
||||
#ifndef SUPPLY_COMP_DIS
|
||||
# define SUPPLY_COMP_DIS 0
|
||||
#endif
|
||||
#ifndef DATA_FORMAT_RTO
|
||||
# define DATA_FORMAT_RTO 0
|
||||
#endif
|
||||
#ifndef LRA_DRIVE_MODE
|
||||
# define LRA_DRIVE_MODE 0
|
||||
#endif
|
||||
#ifndef N_PWM_ANALOG
|
||||
# define N_PWM_ANALOG 0
|
||||
#endif
|
||||
#ifndef LRA_OPEN_LOOP
|
||||
# define LRA_OPEN_LOOP 0
|
||||
#endif
|
||||
|
||||
/* Control 4 settings */
|
||||
#ifndef ZC_DET_TIME
|
||||
# define ZC_DET_TIME 0
|
||||
#endif
|
||||
#ifndef AUTO_CAL_TIME
|
||||
# define AUTO_CAL_TIME 3
|
||||
#endif
|
||||
|
||||
/* register defines -------------------------------------------------------- */
|
||||
#define DRV2605L_BASE_ADDRESS 0x5A /* DRV2605L Base address */
|
||||
#define DRV_STATUS 0x00
|
||||
#define DRV_MODE 0x01
|
||||
#define DRV_RTP_INPUT 0x02
|
||||
#define DRV_LIB_SELECTION 0x03
|
||||
#define DRV_WAVEFORM_SEQ_1 0x04
|
||||
#define DRV_WAVEFORM_SEQ_2 0x05
|
||||
#define DRV_WAVEFORM_SEQ_3 0x06
|
||||
#define DRV_WAVEFORM_SEQ_4 0x07
|
||||
#define DRV_WAVEFORM_SEQ_5 0x08
|
||||
#define DRV_WAVEFORM_SEQ_6 0x09
|
||||
#define DRV_WAVEFORM_SEQ_7 0x0A
|
||||
#define DRV_WAVEFORM_SEQ_8 0x0B
|
||||
#define DRV_GO 0x0C
|
||||
#define DRV_OVERDRIVE_TIME_OFFSET 0x0D
|
||||
#define DRV_SUSTAIN_TIME_OFFSET_P 0x0E
|
||||
#define DRV_SUSTAIN_TIME_OFFSET_N 0x0F
|
||||
#define DRV_BRAKE_TIME_OFFSET 0x10
|
||||
#define DRV_AUDIO_2_VIBE_CTRL 0x11
|
||||
#define DRV_AUDIO_2_VIBE_MIN_IN 0x12
|
||||
#define DRV_AUDIO_2_VIBE_MAX_IN 0x13
|
||||
#define DRV_AUDIO_2_VIBE_MIN_OUTDRV 0x14
|
||||
#define DRV_AUDIO_2_VIBE_MAX_OUTDRV 0x15
|
||||
#define DRV_RATED_VOLT 0x16
|
||||
#define DRV_OVERDRIVE_CLAMP_VOLT 0x17
|
||||
#define DRV_AUTO_CALIB_COMP_RESULT 0x18
|
||||
#define DRV_AUTO_CALIB_BEMF_RESULT 0x19
|
||||
#define DRV_FEEDBACK_CTRL 0x1A
|
||||
#define DRV_CTRL_1 0x1B
|
||||
#define DRV_CTRL_2 0x1C
|
||||
#define DRV_CTRL_3 0x1D
|
||||
#define DRV_CTRL_4 0x1E
|
||||
#define DRV_CTRL_5 0x1F
|
||||
#define DRV_OPEN_LOOP_PERIOD 0x20
|
||||
#define DRV_VBAT_VOLT_MONITOR 0x21
|
||||
#define DRV_LRA_RESONANCE_PERIOD 0x22
|
||||
|
||||
void DRV_init(void);
|
||||
void DRV_write(const uint8_t drv_register, const uint8_t settings);
|
||||
uint8_t DRV_read(const uint8_t regaddress);
|
||||
void DRV_rtp_init(void);
|
||||
void DRV_amplitude(const uint8_t amplitude);
|
||||
void DRV_pulse(const uint8_t sequence);
|
||||
|
||||
typedef enum DRV_EFFECT {
|
||||
clear_sequence = 0,
|
||||
strong_click = 1,
|
||||
strong_click_60 = 2,
|
||||
strong_click_30 = 3,
|
||||
sharp_click = 4,
|
||||
sharp_click_60 = 5,
|
||||
sharp_click_30 = 6,
|
||||
soft_bump = 7,
|
||||
soft_bump_60 = 8,
|
||||
soft_bump_30 = 9,
|
||||
dbl_click = 10,
|
||||
dbl_click_60 = 11,
|
||||
trp_click = 12,
|
||||
soft_fuzz = 13,
|
||||
strong_buzz = 14,
|
||||
alert_750ms = 15,
|
||||
alert_1000ms = 16,
|
||||
strong_click1 = 17,
|
||||
strong_click2_80 = 18,
|
||||
strong_click3_60 = 19,
|
||||
strong_click4_30 = 20,
|
||||
medium_click1 = 21,
|
||||
medium_click2_80 = 22,
|
||||
medium_click3_60 = 23,
|
||||
sharp_tick1 = 24,
|
||||
sharp_tick2_80 = 25,
|
||||
sharp_tick3_60 = 26,
|
||||
sh_dblclick_str = 27,
|
||||
sh_dblclick_str_80 = 28,
|
||||
sh_dblclick_str_60 = 29,
|
||||
sh_dblclick_str_30 = 30,
|
||||
sh_dblclick_med = 31,
|
||||
sh_dblclick_med_80 = 32,
|
||||
sh_dblclick_med_60 = 33,
|
||||
sh_dblsharp_tick = 34,
|
||||
sh_dblsharp_tick_80 = 35,
|
||||
sh_dblsharp_tick_60 = 36,
|
||||
lg_dblclick_str = 37,
|
||||
lg_dblclick_str_80 = 38,
|
||||
lg_dblclick_str_60 = 39,
|
||||
lg_dblclick_str_30 = 40,
|
||||
lg_dblclick_med = 41,
|
||||
lg_dblclick_med_80 = 42,
|
||||
lg_dblclick_med_60 = 43,
|
||||
lg_dblsharp_tick = 44,
|
||||
lg_dblsharp_tick_80 = 45,
|
||||
lg_dblsharp_tick_60 = 46,
|
||||
buzz = 47,
|
||||
buzz_80 = 48,
|
||||
buzz_60 = 49,
|
||||
buzz_40 = 50,
|
||||
buzz_20 = 51,
|
||||
pulsing_strong = 52,
|
||||
pulsing_strong_80 = 53,
|
||||
pulsing_medium = 54,
|
||||
pulsing_medium_80 = 55,
|
||||
pulsing_sharp = 56,
|
||||
pulsing_sharp_80 = 57,
|
||||
transition_click = 58,
|
||||
transition_click_80 = 59,
|
||||
transition_click_60 = 60,
|
||||
transition_click_40 = 61,
|
||||
transition_click_20 = 62,
|
||||
transition_click_10 = 63,
|
||||
transition_hum = 64,
|
||||
transition_hum_80 = 65,
|
||||
transition_hum_60 = 66,
|
||||
transition_hum_40 = 67,
|
||||
transition_hum_20 = 68,
|
||||
transition_hum_10 = 69,
|
||||
transition_rampdown_long_smooth1 = 70,
|
||||
transition_rampdown_long_smooth2 = 71,
|
||||
transition_rampdown_med_smooth1 = 72,
|
||||
transition_rampdown_med_smooth2 = 73,
|
||||
transition_rampdown_short_smooth1 = 74,
|
||||
transition_rampdown_short_smooth2 = 75,
|
||||
transition_rampdown_long_sharp1 = 76,
|
||||
transition_rampdown_long_sharp2 = 77,
|
||||
transition_rampdown_med_sharp1 = 78,
|
||||
transition_rampdown_med_sharp2 = 79,
|
||||
transition_rampdown_short_sharp1 = 80,
|
||||
transition_rampdown_short_sharp2 = 81,
|
||||
transition_rampup_long_smooth1 = 82,
|
||||
transition_rampup_long_smooth2 = 83,
|
||||
transition_rampup_med_smooth1 = 84,
|
||||
transition_rampup_med_smooth2 = 85,
|
||||
transition_rampup_short_smooth1 = 86,
|
||||
transition_rampup_short_smooth2 = 87,
|
||||
transition_rampup_long_sharp1 = 88,
|
||||
transition_rampup_long_sharp2 = 89,
|
||||
transition_rampup_med_sharp1 = 90,
|
||||
transition_rampup_med_sharp2 = 91,
|
||||
transition_rampup_short_sharp1 = 92,
|
||||
transition_rampup_short_sharp2 = 93,
|
||||
transition_rampdown_long_smooth1_50 = 94,
|
||||
transition_rampdown_long_smooth2_50 = 95,
|
||||
transition_rampdown_med_smooth1_50 = 96,
|
||||
transition_rampdown_med_smooth2_50 = 97,
|
||||
transition_rampdown_short_smooth1_50 = 98,
|
||||
transition_rampdown_short_smooth2_50 = 99,
|
||||
transition_rampdown_long_sharp1_50 = 100,
|
||||
transition_rampdown_long_sharp2_50 = 101,
|
||||
transition_rampdown_med_sharp1_50 = 102,
|
||||
transition_rampdown_med_sharp2_50 = 103,
|
||||
transition_rampdown_short_sharp1_50 = 104,
|
||||
transition_rampdown_short_sharp2_50 = 105,
|
||||
transition_rampup_long_smooth1_50 = 106,
|
||||
transition_rampup_long_smooth2_50 = 107,
|
||||
transition_rampup_med_smooth1_50 = 108,
|
||||
transition_rampup_med_smooth2_50 = 109,
|
||||
transition_rampup_short_smooth1_50 = 110,
|
||||
transition_rampup_short_smooth2_50 = 111,
|
||||
transition_rampup_long_sharp1_50 = 112,
|
||||
transition_rampup_long_sharp2_50 = 113,
|
||||
transition_rampup_med_sharp1_50 = 114,
|
||||
transition_rampup_med_sharp2_50 = 115,
|
||||
transition_rampup_short_sharp1_50 = 116,
|
||||
transition_rampup_short_sharp2_50 = 117,
|
||||
long_buzz_for_programmatic_stopping = 118,
|
||||
smooth_hum1_50 = 119,
|
||||
smooth_hum2_40 = 120,
|
||||
smooth_hum3_30 = 121,
|
||||
smooth_hum4_20 = 122,
|
||||
smooth_hum5_10 = 123,
|
||||
drv_effect_max = 124,
|
||||
} DRV_EFFECT;
|
||||
|
||||
/* Register bit array unions */
|
||||
|
||||
typedef union DRVREG_STATUS { /* register 0x00 */
|
||||
uint8_t Byte;
|
||||
struct {
|
||||
uint8_t OC_DETECT : 1; /* set to 1 when overcurrent event is detected */
|
||||
uint8_t OVER_TEMP : 1; /* set to 1 when device exceeds temp threshold */
|
||||
uint8_t FB_STS : 1; /* set to 1 when feedback controller has timed out */
|
||||
/* auto-calibration routine and diagnostic result
|
||||
* result | auto-calibation | diagnostic |
|
||||
* 0 | passed | actuator func normal |
|
||||
* 1 | failed | actuator func fault* |
|
||||
* * actuator is not present or is shorted, timing out, or giving out–of-range back-EMF */
|
||||
uint8_t DIAG_RESULT : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t DEVICE_ID : 3; /* Device IDs 3: DRV2605 4: DRV2604 5: DRV2604L 6: DRV2605L */
|
||||
} Bits;
|
||||
} DRVREG_STATUS;
|
||||
|
||||
typedef union DRVREG_MODE { /* register 0x01 */
|
||||
uint8_t Byte;
|
||||
struct {
|
||||
uint8_t MODE : 3; /* Mode setting */
|
||||
uint8_t : 3;
|
||||
uint8_t STANDBY : 1; /* 0:standby 1:ready */
|
||||
} Bits;
|
||||
} DRVREG_MODE;
|
||||
|
||||
typedef union DRVREG_WAIT {
|
||||
uint8_t Byte;
|
||||
struct {
|
||||
uint8_t WAIT_MODE : 1; /* Set to 1 to interpret as wait for next 7 bits x10ms */
|
||||
uint8_t WAIT_TIME : 7;
|
||||
} Bits;
|
||||
} DRVREG_WAIT;
|
||||
|
||||
typedef union DRVREG_FBR { /* register 0x1A */
|
||||
uint8_t Byte;
|
||||
struct {
|
||||
uint8_t BEMF_GAIN : 2;
|
||||
uint8_t LOOP_GAIN : 2;
|
||||
uint8_t BRAKE_FACTOR : 3;
|
||||
uint8_t ERM_LRA : 1;
|
||||
} Bits;
|
||||
} DRVREG_FBR;
|
||||
|
||||
typedef union DRVREG_CTRL1 { /* register 0x1B */
|
||||
uint8_t Byte;
|
||||
struct {
|
||||
uint8_t C1_DRIVE_TIME : 5;
|
||||
uint8_t C1_AC_COUPLE : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t C1_STARTUP_BOOST : 1;
|
||||
} Bits;
|
||||
} DRVREG_CTRL1;
|
||||
|
||||
typedef union DRVREG_CTRL2 { /* register 0x1C */
|
||||
uint8_t Byte;
|
||||
struct {
|
||||
uint8_t C2_IDISS_TIME : 2;
|
||||
uint8_t C2_BLANKING_TIME : 2;
|
||||
uint8_t C2_SAMPLE_TIME : 2;
|
||||
uint8_t C2_BRAKE_STAB : 1;
|
||||
uint8_t C2_BIDIR_INPUT : 1;
|
||||
} Bits;
|
||||
} DRVREG_CTRL2;
|
||||
|
||||
typedef union DRVREG_CTRL3 { /* register 0x1D */
|
||||
uint8_t Byte;
|
||||
struct {
|
||||
uint8_t C3_LRA_OPEN_LOOP : 1;
|
||||
uint8_t C3_N_PWM_ANALOG : 1;
|
||||
uint8_t C3_LRA_DRIVE_MODE : 1;
|
||||
uint8_t C3_DATA_FORMAT_RTO : 1;
|
||||
uint8_t C3_SUPPLY_COMP_DIS : 1;
|
||||
uint8_t C3_ERM_OPEN_LOOP : 1;
|
||||
uint8_t C3_NG_THRESH : 2;
|
||||
} Bits;
|
||||
} DRVREG_CTRL3;
|
||||
|
||||
typedef union DRVREG_CTRL4 { /* register 0x1E */
|
||||
uint8_t Byte;
|
||||
struct {
|
||||
uint8_t C4_OTP_PROGRAM : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t C4_OTP_STATUS : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t C4_AUTO_CAL_TIME : 2;
|
||||
uint8_t C4_ZC_DET_TIME : 2;
|
||||
} Bits;
|
||||
} DRVREG_CTRL4;
|
||||
|
||||
typedef union DRVREG_CTRL5 { /* register 0x1F */
|
||||
uint8_t Byte;
|
||||
struct {
|
||||
uint8_t C5_IDISS_TIME : 2;
|
||||
uint8_t C5_BLANKING_TIME : 2;
|
||||
uint8_t C5_PLAYBACK_INTERVAL : 1;
|
||||
uint8_t C5_LRA_AUTO_OPEN_LOOP : 1;
|
||||
uint8_t C5_AUTO_OL_CNT : 2;
|
||||
} Bits;
|
||||
} DRVREG_CTRL5;
|
126
drivers/haptic/drv2605l.c
Normal file
126
drivers/haptic/drv2605l.c
Normal file
@ -0,0 +1,126 @@
|
||||
/* Copyright 2018 ishtob
|
||||
* Driver for DRV2605L written for QMK
|
||||
*
|
||||
* 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 "drv2605l.h"
|
||||
#include "i2c_master.h"
|
||||
#include <math.h>
|
||||
|
||||
uint8_t drv2605l_write_buffer[2];
|
||||
uint8_t drv2605l_read_buffer;
|
||||
|
||||
void drv2605l_write(uint8_t reg_addr, uint8_t data) {
|
||||
drv2605l_write_buffer[0] = reg_addr;
|
||||
drv2605l_write_buffer[1] = data;
|
||||
i2c_transmit(DRV2605L_I2C_ADDRESS << 1, drv2605l_write_buffer, 2, 100);
|
||||
}
|
||||
|
||||
uint8_t drv2605l_read(uint8_t reg_addr) {
|
||||
i2c_readReg(DRV2605L_I2C_ADDRESS << 1, reg_addr, &drv2605l_read_buffer, 1, 100);
|
||||
|
||||
return drv2605l_read_buffer;
|
||||
}
|
||||
|
||||
void drv2605l_init(void) {
|
||||
i2c_init();
|
||||
/* 0x07 sets DRV2605 into calibration mode */
|
||||
drv2605l_write(DRV2605L_REG_MODE, 0x07);
|
||||
|
||||
// drv2605l_write(DRV2605L_REG_FEEDBACK_CTRL,0xB6);
|
||||
|
||||
#if DRV2605L_FB_ERM_LRA == 0
|
||||
/* ERM settings */
|
||||
drv2605l_write(DRV2605L_REG_RATED_VOLTAGE, (DRV2605L_RATED_VOLTAGE / 21.33) * 1000);
|
||||
# if DRV2605L_ERM_OPEN_LOOP == 0
|
||||
drv2605l_write(DRV2605L_REG_OVERDRIVE_CLAMP_VOLTAGE, (((DRV2605L_V_PEAK * (DRV2605L_DRIVE_TIME + DRV2605L_BLANKING_TIME + DRV2605L_IDISS_TIME)) / 0.02133) / (DRV2605L_DRIVE_TIME - 0.0003)));
|
||||
# elif DRV2605L_ERM_OPEN_LOOP == 1
|
||||
drv2605l_write(DRV2605L_REG_OVERDRIVE_CLAMP_VOLTAGE, (DRV2605L_V_PEAK / 0.02196));
|
||||
# endif
|
||||
#elif DRV2605L_FB_ERM_LRA == 1
|
||||
drv2605l_write(DRV2605L_REG_RATED_VOLTAGE, ((DRV2605L_V_RMS * sqrt(1 - ((4 * ((150 + (DRV2605L_SAMPLE_TIME * 50)) * 0.000001)) + 0.0003) * DRV2605L_F_LRA) / 0.02071)));
|
||||
# if DRV2605L_LRA_OPEN_LOOP == 0
|
||||
drv2605l_write(DRV2605L_REG_OVERDRIVE_CLAMP_VOLTAGE, ((DRV2605L_V_PEAK / sqrt(1 - (DRV2605L_F_LRA * 0.0008)) / 0.02133)));
|
||||
# elif DRV2605L_LRA_OPEN_LOOP == 1
|
||||
drv2605l_write(DRV2605L_REG_OVERDRIVE_CLAMP_VOLTAGE, (DRV2605L_V_PEAK / 0.02196));
|
||||
# endif
|
||||
#endif
|
||||
|
||||
drv2605l_reg_feedback_ctrl_t reg_feedback_ctrl;
|
||||
reg_feedback_ctrl.bits.ERM_LRA = DRV2605L_FB_ERM_LRA;
|
||||
reg_feedback_ctrl.bits.BRAKE_FACTOR = DRV2605L_FB_BRAKEFACTOR;
|
||||
reg_feedback_ctrl.bits.LOOP_GAIN = DRV2605L_FB_LOOPGAIN;
|
||||
reg_feedback_ctrl.bits.BEMF_GAIN = 0; /* auto-calibration populates this field*/
|
||||
drv2605l_write(DRV2605L_REG_FEEDBACK_CTRL, (uint8_t)reg_feedback_ctrl.raw);
|
||||
|
||||
drv2605l_reg_ctrl1_t reg_ctrl1;
|
||||
reg_ctrl1.bits.C1_DRIVE_TIME = DRV2605L_DRIVE_TIME;
|
||||
reg_ctrl1.bits.C1_AC_COUPLE = DRV2605L_AC_COUPLE;
|
||||
reg_ctrl1.bits.C1_STARTUP_BOOST = DRV2605L_STARTUP_BOOST;
|
||||
drv2605l_write(DRV2605L_REG_CTRL1, (uint8_t)reg_ctrl1.raw);
|
||||
|
||||
drv2605l_reg_ctrl2_t reg_ctrl2;
|
||||
reg_ctrl2.bits.C2_BIDIR_INPUT = DRV2605L_BIDIR_INPUT;
|
||||
reg_ctrl2.bits.C2_BRAKE_STAB = DRV2605L_BRAKE_STAB;
|
||||
reg_ctrl2.bits.C2_SAMPLE_TIME = DRV2605L_SAMPLE_TIME;
|
||||
reg_ctrl2.bits.C2_BLANKING_TIME = DRV2605L_BLANKING_TIME;
|
||||
reg_ctrl2.bits.C2_IDISS_TIME = DRV2605L_IDISS_TIME;
|
||||
drv2605l_write(DRV2605L_REG_CTRL2, (uint8_t)reg_ctrl2.raw);
|
||||
|
||||
drv2605l_reg_ctrl3_t reg_ctrl3;
|
||||
reg_ctrl3.bits.C3_LRA_OPEN_LOOP = DRV2605L_LRA_OPEN_LOOP;
|
||||
reg_ctrl3.bits.C3_N_PWM_ANALOG = DRV2605L_N_PWM_ANALOG;
|
||||
reg_ctrl3.bits.C3_LRA_DRIVE_MODE = DRV2605L_LRA_DRIVE_MODE;
|
||||
reg_ctrl3.bits.C3_DATA_FORMAT_RTO = DRV2605L_DATA_FORMAT_RTO;
|
||||
reg_ctrl3.bits.C3_SUPPLY_COMP_DIS = DRV2605L_SUPPLY_COMP_DIS;
|
||||
reg_ctrl3.bits.C3_ERM_OPEN_LOOP = DRV2605L_ERM_OPEN_LOOP;
|
||||
reg_ctrl3.bits.C3_NG_THRESH = DRV2605L_NG_THRESH;
|
||||
drv2605l_write(DRV2605L_REG_CTRL3, (uint8_t)reg_ctrl3.raw);
|
||||
|
||||
drv2605l_reg_ctrl4_t reg_ctrl4;
|
||||
reg_ctrl4.bits.C4_ZC_DET_TIME = DRV2605L_ZC_DET_TIME;
|
||||
reg_ctrl4.bits.C4_AUTO_CAL_TIME = DRV2605L_AUTO_CAL_TIME;
|
||||
drv2605l_write(DRV2605L_REG_CTRL4, (uint8_t)reg_ctrl4.raw);
|
||||
|
||||
drv2605l_write(DRV2605L_REG_LIBRARY_SELECTION, DRV2605L_LIBRARY);
|
||||
|
||||
drv2605l_write(DRV2605L_REG_GO, 0x01);
|
||||
|
||||
/* 0x00 sets DRV2605 out of standby and to use internal trigger
|
||||
* 0x01 sets DRV2605 out of standby and to use external trigger */
|
||||
drv2605l_write(DRV2605L_REG_MODE, 0x00);
|
||||
|
||||
// Play greeting sequence
|
||||
drv2605l_write(DRV2605L_REG_GO, 0x00);
|
||||
drv2605l_write(DRV2605L_REG_WAVEFORM_SEQUENCER_1, DRV2605L_GREETING);
|
||||
drv2605l_write(DRV2605L_REG_GO, 0x01);
|
||||
}
|
||||
|
||||
void drv2605l_rtp_init(void) {
|
||||
drv2605l_write(DRV2605L_REG_GO, 0x00);
|
||||
drv2605l_write(DRV2605L_REG_RTP_INPUT, 20); // 20 is the lowest value I've found where haptics can still be felt.
|
||||
drv2605l_write(DRV2605L_REG_MODE, 0x05);
|
||||
drv2605l_write(DRV2605L_REG_GO, 0x01);
|
||||
}
|
||||
|
||||
void drv2605l_amplitude(uint8_t amplitude) {
|
||||
drv2605l_write(DRV2605L_REG_RTP_INPUT, amplitude);
|
||||
}
|
||||
|
||||
void drv2605l_pulse(uint8_t sequence) {
|
||||
drv2605l_write(DRV2605L_REG_GO, 0x00);
|
||||
drv2605l_write(DRV2605L_REG_WAVEFORM_SEQUENCER_1, sequence);
|
||||
drv2605l_write(DRV2605L_REG_GO, 0x01);
|
||||
}
|
362
drivers/haptic/drv2605l.h
Normal file
362
drivers/haptic/drv2605l.h
Normal file
@ -0,0 +1,362 @@
|
||||
/* Copyright 2018 ishtob
|
||||
* Driver for DRV2605L written for QMK
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Initialization settings
|
||||
|
||||
* Feedback Control Settings */
|
||||
#ifndef DRV2605L_FB_ERM_LRA
|
||||
# define DRV2605L_FB_ERM_LRA 1 /* For ERM:0 or LRA:1*/
|
||||
#endif
|
||||
#ifndef DRV2605L_FB_BRAKEFACTOR
|
||||
# define DRV2605L_FB_BRAKEFACTOR 3 /* For 1x:0, 2x:1, 3x:2, 4x:3, 6x:4, 8x:5, 16x:6, Disable Braking:7 */
|
||||
#endif
|
||||
#ifndef DRV2605L_FB_LOOPGAIN
|
||||
# define DRV2605L_FB_LOOPGAIN 1 /* For Low:0, Medium:1, High:2, Very High:3 */
|
||||
#endif
|
||||
|
||||
/* LRA specific settings */
|
||||
#if DRV2605L_FB_ERM_LRA == 1
|
||||
# ifndef DRV2605L_V_RMS
|
||||
# define DRV2605L_V_RMS 2.0
|
||||
# endif
|
||||
# ifndef DRV2605L_V_PEAK
|
||||
# define DRV2605L_V_PEAK 2.1
|
||||
# endif
|
||||
# ifndef DRV2605L_F_LRA
|
||||
# define DRV2605L_F_LRA 205
|
||||
# endif
|
||||
# ifndef DRV2605L_RATED_VOLTAGE
|
||||
# define DRV2605L_RATED_VOLTAGE 2 /* 2v as safe range in case device voltage is not set */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef DRV2605L_RATED_VOLTAGE
|
||||
# define DRV2605L_RATED_VOLTAGE 2 /* 2v as safe range in case device voltage is not set */
|
||||
#endif
|
||||
#ifndef DRV2605L_V_PEAK
|
||||
# define DRV2605L_V_PEAK 2.8
|
||||
#endif
|
||||
|
||||
/* Library Selection */
|
||||
#ifndef DRV2605L_LIBRARY
|
||||
# if DRV2605L_FB_ERM_LRA == 1
|
||||
# define DRV2605L_LIBRARY 6 /* For Empty:0' TS2200 library A to D:1-5, LRA Library: 6 */
|
||||
# else
|
||||
# define DRV2605L_LIBRARY 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef DRV2605L_GREETING
|
||||
# define DRV2605L_GREETING DRV2605L_EFFECT_750_MS_ALERT_100
|
||||
#endif
|
||||
#ifndef DRV2605L_DEFAULT_MODE
|
||||
# define DRV2605L_DEFAULT_MODE DRV2605L_EFFECT_STRONG_CLICK_1_100
|
||||
#endif
|
||||
|
||||
/* Control 1 register settings */
|
||||
#ifndef DRV2605L_DRIVE_TIME
|
||||
# define DRV2605L_DRIVE_TIME 25
|
||||
#endif
|
||||
#ifndef DRV2605L_AC_COUPLE
|
||||
# define DRV2605L_AC_COUPLE 0
|
||||
#endif
|
||||
#ifndef DRV2605L_STARTUP_BOOST
|
||||
# define DRV2605L_STARTUP_BOOST 1
|
||||
#endif
|
||||
|
||||
/* Control 2 Settings */
|
||||
#ifndef DRV2605L_BIDIR_INPUT
|
||||
# define DRV2605L_BIDIR_INPUT 1
|
||||
#endif
|
||||
#ifndef DRV2605L_BRAKE_STAB
|
||||
# define DRV2605L_BRAKE_STAB 1 /* Loopgain is reduced when braking is almost complete to improve stability */
|
||||
#endif
|
||||
#ifndef DRV2605L_SAMPLE_TIME
|
||||
# define DRV2605L_SAMPLE_TIME 3
|
||||
#endif
|
||||
#ifndef DRV2605L_BLANKING_TIME
|
||||
# define DRV2605L_BLANKING_TIME 1
|
||||
#endif
|
||||
#ifndef DRV2605L_IDISS_TIME
|
||||
# define DRV2605L_IDISS_TIME 1
|
||||
#endif
|
||||
|
||||
/* Control 3 settings */
|
||||
#ifndef DRV2605L_NG_THRESH
|
||||
# define DRV2605L_NG_THRESH 2
|
||||
#endif
|
||||
#ifndef DRV2605L_ERM_OPEN_LOOP
|
||||
# define DRV2605L_ERM_OPEN_LOOP 1
|
||||
#endif
|
||||
#ifndef DRV2605L_SUPPLY_COMP_DIS
|
||||
# define DRV2605L_SUPPLY_COMP_DIS 0
|
||||
#endif
|
||||
#ifndef DRV2605L_DATA_FORMAT_RTO
|
||||
# define DRV2605L_DATA_FORMAT_RTO 0
|
||||
#endif
|
||||
#ifndef DRV2605L_LRA_DRIVE_MODE
|
||||
# define DRV2605L_LRA_DRIVE_MODE 0
|
||||
#endif
|
||||
#ifndef DRV2605L_N_PWM_ANALOG
|
||||
# define DRV2605L_N_PWM_ANALOG 0
|
||||
#endif
|
||||
#ifndef DRV2605L_LRA_OPEN_LOOP
|
||||
# define DRV2605L_LRA_OPEN_LOOP 0
|
||||
#endif
|
||||
|
||||
/* Control 4 settings */
|
||||
#ifndef DRV2605L_ZC_DET_TIME
|
||||
# define DRV2605L_ZC_DET_TIME 0
|
||||
#endif
|
||||
#ifndef DRV2605L_AUTO_CAL_TIME
|
||||
# define DRV2605L_AUTO_CAL_TIME 3
|
||||
#endif
|
||||
|
||||
#define DRV2605L_I2C_ADDRESS 0x5A
|
||||
|
||||
#define DRV2605L_REG_STATUS 0x00
|
||||
#define DRV2605L_REG_MODE 0x01
|
||||
#define DRV2605L_REG_RTP_INPUT 0x02
|
||||
#define DRV2605L_REG_LIBRARY_SELECTION 0x03
|
||||
#define DRV2605L_REG_WAVEFORM_SEQUENCER_1 0x04
|
||||
#define DRV2605L_REG_WAVEFORM_SEQUENCER_2 0x05
|
||||
#define DRV2605L_REG_WAVEFORM_SEQUENCER_3 0x06
|
||||
#define DRV2605L_REG_WAVEFORM_SEQUENCER_4 0x07
|
||||
#define DRV2605L_REG_WAVEFORM_SEQUENCER_5 0x08
|
||||
#define DRV2605L_REG_WAVEFORM_SEQUENCER_6 0x09
|
||||
#define DRV2605L_REG_WAVEFORM_SEQUENCER_7 0x0A
|
||||
#define DRV2605L_REG_WAVEFORM_SEQUENCER_8 0x0B
|
||||
#define DRV2605L_REG_GO 0x0C
|
||||
#define DRV2605L_REG_OVERDRIVE_TIME_OFFSET 0x0D
|
||||
#define DRV2605L_REG_SUSTAIN_TIME_OFFSET_P 0x0E
|
||||
#define DRV2605L_REG_SUSTAIN_TIME_OFFSET_N 0x0F
|
||||
#define DRV2605L_REG_BRAKE_TIME_OFFSET 0x10
|
||||
#define DRV2605L_REG_AUDIO_TO_VIBE_CTRL 0x11
|
||||
#define DRV2605L_REG_AUDIO_TO_VIBE_MIN_INPUT 0x12
|
||||
#define DRV2605L_REG_AUDIO_TO_VIBE_MAX_INPUT 0x13
|
||||
#define DRV2605L_REG_AUDIO_TO_VIBE_MIN_OUTPUT_DRIVE 0x14
|
||||
#define DRV2605L_REG_AUDIO_TO_VIBE_MAX_OUTPUT_DRIVE 0x15
|
||||
#define DRV2605L_REG_RATED_VOLTAGE 0x16
|
||||
#define DRV2605L_REG_OVERDRIVE_CLAMP_VOLTAGE 0x17
|
||||
#define DRV2605L_REG_AUTO_CALIBRATION_COMPENSATION_RESULT 0x18
|
||||
#define DRV2605L_REG_AUTO_CALIBRATION_BACK_EMF_RESULT 0x19
|
||||
#define DRV2605L_REG_FEEDBACK_CTRL 0x1A
|
||||
#define DRV2605L_REG_CTRL1 0x1B
|
||||
#define DRV2605L_REG_CTRL2 0x1C
|
||||
#define DRV2605L_REG_CTRL3 0x1D
|
||||
#define DRV2605L_REG_CTRL4 0x1E
|
||||
#define DRV2605L_REG_CTRL5 0x1F
|
||||
#define DRV2605L_REG_LRA_OPEN_LOOP_PERIOD 0x20
|
||||
#define DRV2605L_REG_VBAT_VOLTAGE_MONITOR 0x21
|
||||
#define DRV2605L_REG_LRA_RESONANCE_PERIOD 0x22
|
||||
|
||||
void drv2605l_init(void);
|
||||
void drv2605l_write(const uint8_t reg_addr, const uint8_t data);
|
||||
uint8_t drv2605l_read(const uint8_t reg_addr);
|
||||
void drv2605l_rtp_init(void);
|
||||
void drv2605l_amplitude(const uint8_t amplitude);
|
||||
void drv2605l_pulse(const uint8_t sequence);
|
||||
|
||||
typedef enum drv2605l_effect_t {
|
||||
DRV2605L_EFFECT_CLEAR_SEQUENCE,
|
||||
DRV2605L_EFFECT_STRONG_CLICK_100,
|
||||
DRV2605L_EFFECT_STRONG_CLICK_60,
|
||||
DRV2605L_EFFECT_STRONG_CLICK_30,
|
||||
DRV2605L_EFFECT_SHARP_CLICK_100,
|
||||
DRV2605L_EFFECT_SHARP_CLICK_60,
|
||||
DRV2605L_EFFECT_SHARP_CLICK_30,
|
||||
DRV2605L_EFFECT_SOFT_BUMP_100,
|
||||
DRV2605L_EFFECT_SOFT_BUMP_60,
|
||||
DRV2605L_EFFECT_SOFT_BUMP_30,
|
||||
DRV2605L_EFFECT_DOUBLE_CLICK_100,
|
||||
DRV2605L_EFFECT_DOUBLE_CLICK_60,
|
||||
DRV2605L_EFFECT_TRIPLE_CLICK_100,
|
||||
DRV2605L_EFFECT_SOFT_FUZZ_60,
|
||||
DRV2605L_EFFECT_STRONG_BUZZ_100,
|
||||
DRV2605L_EFFECT_750_MS_ALERT_100,
|
||||
DRV2605L_EFFECT_1000_MS_ALERT_100,
|
||||
DRV2605L_EFFECT_STRONG_CLICK_1_100,
|
||||
DRV2605L_EFFECT_STRONG_CLICK_2_80,
|
||||
DRV2605L_EFFECT_STRONG_CLICK_3_60,
|
||||
DRV2605L_EFFECT_STRONG_CLICK_4_30,
|
||||
DRV2605L_EFFECT_MEDIUM_CLICK_1_100,
|
||||
DRV2605L_EFFECT_MEDIUM_CLICK_2_80,
|
||||
DRV2605L_EFFECT_MEDIUM_CLICK_3_60,
|
||||
DRV2605L_EFFECT_SHARP_TICK_1_100,
|
||||
DRV2605L_EFFECT_SHARP_TICK_2_80,
|
||||
DRV2605L_EFFECT_SHARP_TICK_3_60,
|
||||
DRV2605L_EFFECT_SHORT_DOUBLE_CLICK_STRONG_1_100,
|
||||
DRV2605L_EFFECT_SHORT_DOUBLE_CLICK_STRONG_2_80,
|
||||
DRV2605L_EFFECT_SHORT_DOUBLE_CLICK_STRONG_3_60,
|
||||
DRV2605L_EFFECT_SHORT_DOUBLE_CLICK_STRONG_4_30,
|
||||
DRV2605L_EFFECT_SHORT_DOUBLE_CLICK_MEDIUM_1_100,
|
||||
DRV2605L_EFFECT_SHORT_DOUBLE_CLICK_MEDIUM_2_80,
|
||||
DRV2605L_EFFECT_SHORT_DOUBLE_CLICK_MEDIUM_3_60,
|
||||
DRV2605L_EFFECT_SHORT_DOUBLE_SHARP_TICK_1_100,
|
||||
DRV2605L_EFFECT_SHORT_DOUBLE_SHARP_TICK_2_80,
|
||||
DRV2605L_EFFECT_SHORT_DOUBLE_SHARP_TICK_3_60,
|
||||
DRV2605L_EFFECT_LONG_DOUBLE_SHARP_CLICK_STRONG_1_100,
|
||||
DRV2605L_EFFECT_LONG_DOUBLE_SHARP_CLICK_STRONG_2_80,
|
||||
DRV2605L_EFFECT_LONG_DOUBLE_SHARP_CLICK_STRONG_3_60,
|
||||
DRV2605L_EFFECT_LONG_DOUBLE_SHARP_CLICK_STRONG_4_30,
|
||||
DRV2605L_EFFECT_LONG_DOUBLE_SHARP_CLICK_MEDIUM_1_100,
|
||||
DRV2605L_EFFECT_LONG_DOUBLE_SHARP_CLICK_MEDIUM_2_80,
|
||||
DRV2605L_EFFECT_LONG_DOUBLE_SHARP_CLICK_MEDIUM_3_60,
|
||||
DRV2605L_EFFECT_LONG_DOUBLE_SHARP_TICK_1_100,
|
||||
DRV2605L_EFFECT_LONG_DOUBLE_SHARP_TICK_2_80,
|
||||
DRV2605L_EFFECT_LONG_DOUBLE_SHARP_TICK_3_60,
|
||||
DRV2605L_EFFECT_BUZZ_1_100,
|
||||
DRV2605L_EFFECT_BUZZ_2_80,
|
||||
DRV2605L_EFFECT_BUZZ_3_60,
|
||||
DRV2605L_EFFECT_BUZZ_4_40,
|
||||
DRV2605L_EFFECT_BUZZ_5_20,
|
||||
DRV2605L_EFFECT_PULSING_STRONG_1_100,
|
||||
DRV2605L_EFFECT_PULSING_STRONG_2_60,
|
||||
DRV2605L_EFFECT_PULSING_MEDIUM_1_100,
|
||||
DRV2605L_EFFECT_PULSING_MEDIUM_2_60,
|
||||
DRV2605L_EFFECT_PULSING_SHARP_1_100,
|
||||
DRV2605L_EFFECT_PULSING_SHARP_2_60,
|
||||
DRV2605L_EFFECT_TRANSITION_CLICK_1_100,
|
||||
DRV2605L_EFFECT_TRANSITION_CLICK_2_80,
|
||||
DRV2605L_EFFECT_TRANSITION_CLICK_3_60,
|
||||
DRV2605L_EFFECT_TRANSITION_CLICK_4_40,
|
||||
DRV2605L_EFFECT_TRANSITION_CLICK_5_20,
|
||||
DRV2605L_EFFECT_TRANSITION_CLICK_6_10,
|
||||
DRV2605L_EFFECT_TRANSITION_HUM_1_100,
|
||||
DRV2605L_EFFECT_TRANSITION_HUM_2_80,
|
||||
DRV2605L_EFFECT_TRANSITION_HUM_3_60,
|
||||
DRV2605L_EFFECT_TRANSITION_HUM_4_40,
|
||||
DRV2605L_EFFECT_TRANSITION_HUM_5_20,
|
||||
DRV2605L_EFFECT_TRANSITION_HUM_6_10,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_LONG_SMOOTH_1_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_LONG_SMOOTH_2_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_MEDIUM_SMOOTH_1_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_MEDIUM_SMOOTH_2_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_SHORT_SMOOTH_1_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_SHORT_SMOOTH_2_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_LONG_SHARP_1_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_LONG_SHARP_2_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_MEDIUM_SHARP_1_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_MEDIUM_SHARP_2_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_SHORT_SHARP_1_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_SHORT_SHARP_2_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_LONG_SMOOTH_1_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_LONG_SMOOTH_2_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_MEDIUM_SMOOTH_1_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_MEDIUM_SMOOTH_2_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_SHORT_SMOOTH_1_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_SHORT_SMOOTH_2_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_LONG_SHARP_1_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_LONG_SHARP_2_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_MEDIUM_SHARP_1_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_MEDIUM_SHARP_2_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_SHORT_SHARP_1_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_SHORT_SHARP_2_100,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_LONG_SMOOTH_1_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_LONG_SMOOTH_2_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_MEDIUM_SMOOTH_1_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_MEDIUM_SMOOTH_2_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_SHORT_SMOOTH_1_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_SHORT_SMOOTH_2_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_LONG_SHARP_1_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_LONG_SHARP_2_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_MEDIUM_SHARP_1_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_MEDIUM_SHARP_2_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_SHORT_SHARP_1_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_SHORT_SHARP_2_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_LONG_SMOOTH_1_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_LONG_SMOOTH_2_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_MEDIUM_SMOOTH_1_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_MEDIUM_SMOOTH_2_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_SHORT_SMOOTH_1_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_SHORT_SMOOTH_2_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_LONG_SHARP_1_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_LONG_SHARP_2_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_MEDIUM_SHARP_1_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_MEDIUM_SHARP_2_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_SHORT_SHARP_1_50,
|
||||
DRV2605L_EFFECT_TRANSITION_RAMP_UP_SHORT_SHARP_2_50,
|
||||
DRV2605L_EFFECT_LONG_BUZZ_FOR_PROGRAMMATIC_STOPPING,
|
||||
DRV2605L_EFFECT_SMOOTH_HUM_1_50,
|
||||
DRV2605L_EFFECT_SMOOTH_HUM_2_40,
|
||||
DRV2605L_EFFECT_SMOOTH_HUM_3_30,
|
||||
DRV2605L_EFFECT_SMOOTH_HUM_4_20,
|
||||
DRV2605L_EFFECT_SMOOTH_HUM_5_10,
|
||||
DRV2605L_EFFECT_COUNT
|
||||
} drv2605l_effect_t;
|
||||
|
||||
/* Register bit array unions */
|
||||
|
||||
typedef union { /* register 0x1A */
|
||||
uint8_t raw;
|
||||
struct {
|
||||
uint8_t BEMF_GAIN : 2;
|
||||
uint8_t LOOP_GAIN : 2;
|
||||
uint8_t BRAKE_FACTOR : 3;
|
||||
uint8_t ERM_LRA : 1;
|
||||
} bits;
|
||||
} drv2605l_reg_feedback_ctrl_t;
|
||||
|
||||
typedef union { /* register 0x1B */
|
||||
uint8_t raw;
|
||||
struct {
|
||||
uint8_t C1_DRIVE_TIME : 5;
|
||||
uint8_t C1_AC_COUPLE : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t C1_STARTUP_BOOST : 1;
|
||||
} bits;
|
||||
} drv2605l_reg_ctrl1_t;
|
||||
|
||||
typedef union { /* register 0x1C */
|
||||
uint8_t raw;
|
||||
struct {
|
||||
uint8_t C2_IDISS_TIME : 2;
|
||||
uint8_t C2_BLANKING_TIME : 2;
|
||||
uint8_t C2_SAMPLE_TIME : 2;
|
||||
uint8_t C2_BRAKE_STAB : 1;
|
||||
uint8_t C2_BIDIR_INPUT : 1;
|
||||
} bits;
|
||||
} drv2605l_reg_ctrl2_t;
|
||||
|
||||
typedef union { /* register 0x1D */
|
||||
uint8_t raw;
|
||||
struct {
|
||||
uint8_t C3_LRA_OPEN_LOOP : 1;
|
||||
uint8_t C3_N_PWM_ANALOG : 1;
|
||||
uint8_t C3_LRA_DRIVE_MODE : 1;
|
||||
uint8_t C3_DATA_FORMAT_RTO : 1;
|
||||
uint8_t C3_SUPPLY_COMP_DIS : 1;
|
||||
uint8_t C3_ERM_OPEN_LOOP : 1;
|
||||
uint8_t C3_NG_THRESH : 2;
|
||||
} bits;
|
||||
} drv2605l_reg_ctrl3_t;
|
||||
|
||||
typedef union { /* register 0x1E */
|
||||
uint8_t raw;
|
||||
struct {
|
||||
uint8_t C4_OTP_PROGRAM : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t C4_OTP_STATUS : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t C4_AUTO_CAL_TIME : 2;
|
||||
uint8_t C4_ZC_DET_TIME : 2;
|
||||
} bits;
|
||||
} drv2605l_reg_ctrl4_t;
|
@ -16,14 +16,14 @@
|
||||
*/
|
||||
|
||||
#include "apa102.h"
|
||||
#include "quantum.h"
|
||||
#include "gpio.h"
|
||||
|
||||
#ifndef APA102_NOPS
|
||||
# if defined(__AVR__)
|
||||
# define APA102_NOPS 0 // AVR at 16 MHz already spends 62.5 ns per clock, so no extra delay is needed
|
||||
# elif defined(PROTOCOL_CHIBIOS)
|
||||
|
||||
# include "hal.h"
|
||||
# include "chibios_config.h"
|
||||
# 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
|
||||
# else
|
||||
|
@ -71,7 +71,7 @@
|
||||
uint8_t g_pwm_buffer[DRIVER_COUNT][AW_PWM_REGISTER_COUNT];
|
||||
bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
|
||||
|
||||
bool AW20216_write(pin_t cs_pin, uint8_t page, uint8_t reg, uint8_t* data, uint8_t len) {
|
||||
bool aw20216_write(pin_t cs_pin, uint8_t page, uint8_t reg, uint8_t* data, uint8_t len) {
|
||||
static uint8_t s_spi_transfer_buffer[2] = {0};
|
||||
|
||||
if (!spi_start(cs_pin, false, AW_SPI_MODE, AW_SPI_DIVISOR)) {
|
||||
@ -96,70 +96,73 @@ bool AW20216_write(pin_t cs_pin, uint8_t page, uint8_t reg, uint8_t* data, uint8
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool AW20216_write_register(pin_t cs_pin, uint8_t page, uint8_t reg, uint8_t value) {
|
||||
static inline bool aw20216_write_register(pin_t cs_pin, uint8_t page, uint8_t reg, uint8_t value) {
|
||||
// Little wrapper so callers need not care about sending a buffer
|
||||
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);
|
||||
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
|
||||
for (uint8_t i = 0; i < AW_PWM_REGISTER_COUNT; i++) {
|
||||
AW20216_write_register(cs_pin, AW_PAGE_SCALING, i, AW_SCALING_MAX);
|
||||
aw20216_write_register(cs_pin, AW_PAGE_SCALING, i, AW_SCALING_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void AW20216_init_current_limit(pin_t cs_pin) {
|
||||
static inline void aw20216_init_current_limit(pin_t cs_pin) {
|
||||
// Push config
|
||||
AW20216_write_register(cs_pin, AW_PAGE_FUNCTION, AW_REG_GLOBALCURRENT, AW_GLOBAL_CURRENT_MAX);
|
||||
aw20216_write_register(cs_pin, AW_PAGE_FUNCTION, AW_REG_GLOBALCURRENT, AW_GLOBAL_CURRENT_MAX);
|
||||
}
|
||||
|
||||
static inline void AW20216_soft_enable(pin_t cs_pin) {
|
||||
static inline void aw20216_soft_enable(pin_t cs_pin) {
|
||||
// Push config
|
||||
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);
|
||||
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);
|
||||
writePinHigh(en_pin);
|
||||
|
||||
AW20216_soft_reset(cs_pin);
|
||||
aw20216_soft_reset(cs_pin);
|
||||
wait_ms(2);
|
||||
|
||||
// Drivers should start with all scaling and PWM registers as off
|
||||
AW20216_init_current_limit(cs_pin);
|
||||
AW20216_init_scaling(cs_pin);
|
||||
aw20216_init_current_limit(cs_pin);
|
||||
aw20216_init_scaling(cs_pin);
|
||||
|
||||
AW20216_soft_enable(cs_pin);
|
||||
AW20216_auto_lowpower(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) {
|
||||
aw_led led;
|
||||
memcpy_P(&led, (&g_aw_leds[index]), sizeof(led));
|
||||
|
||||
if (g_pwm_buffer[led.driver][led.r] == red && g_pwm_buffer[led.driver][led.g] == green && g_pwm_buffer[led.driver][led.b] == blue) {
|
||||
return;
|
||||
}
|
||||
g_pwm_buffer[led.driver][led.r] = red;
|
||||
g_pwm_buffer[led.driver][led.g] = green;
|
||||
g_pwm_buffer[led.driver][led.b] = blue;
|
||||
g_pwm_buffer_update_required[led.driver] = true;
|
||||
}
|
||||
|
||||
void AW20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
void aw20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
for (uint8_t i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
|
||||
AW20216_set_color(i, red, green, blue);
|
||||
aw20216_set_color(i, red, green, blue);
|
||||
}
|
||||
}
|
||||
|
||||
void AW20216_update_pwm_buffers(pin_t cs_pin, uint8_t index) {
|
||||
void aw20216_update_pwm_buffers(pin_t cs_pin, uint8_t index) {
|
||||
if (g_pwm_buffer_update_required[index]) {
|
||||
AW20216_write(cs_pin, AW_PAGE_PWM, 0, g_pwm_buffer[index], AW_PWM_REGISTER_COUNT);
|
||||
aw20216_write(cs_pin, AW_PAGE_PWM, 0, g_pwm_buffer[index], AW_PWM_REGISTER_COUNT);
|
||||
}
|
||||
g_pwm_buffer_update_required[index] = false;
|
||||
}
|
||||
|
@ -30,10 +30,10 @@ typedef struct aw_led {
|
||||
|
||||
extern const aw_led PROGMEM g_aw_leds[RGB_MATRIX_LED_COUNT];
|
||||
|
||||
void AW20216_init(pin_t cs_pin, pin_t en_pin);
|
||||
void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void AW20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
|
||||
void AW20216_update_pwm_buffers(pin_t cs_pin, uint8_t index);
|
||||
void aw20216_init(pin_t cs_pin, pin_t en_pin);
|
||||
void aw20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void aw20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
|
||||
void aw20216_update_pwm_buffers(pin_t cs_pin, uint8_t index);
|
||||
|
||||
#define CS1_SW1 0x00
|
||||
#define CS2_SW1 0x01
|
||||
|
@ -42,7 +42,7 @@ uint8_t g_twi_transfer_buffer[20];
|
||||
// The control buffers match the PG0 LED On/Off registers.
|
||||
// Storing them like this is optimal for I2C transfers to the registers.
|
||||
// We could optimize this and take out the unused registers from these
|
||||
// buffers and the transfers in CKLED2001_write_pwm_buffer() but it's
|
||||
// buffers and the transfers in ckled2001_write_pwm_buffer() but it's
|
||||
// probably not worth the extra complexity.
|
||||
uint8_t g_pwm_buffer[DRIVER_COUNT][192];
|
||||
bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
|
||||
@ -50,7 +50,7 @@ bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
|
||||
uint8_t g_led_control_registers[DRIVER_COUNT][24] = {0};
|
||||
bool g_led_control_registers_update_required[DRIVER_COUNT] = {false};
|
||||
|
||||
bool CKLED2001_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
bool ckled2001_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
// If the transaction fails function returns false.
|
||||
g_twi_transfer_buffer[0] = reg;
|
||||
g_twi_transfer_buffer[1] = data;
|
||||
@ -69,7 +69,7 @@ bool CKLED2001_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKLED2001_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
bool ckled2001_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
// Assumes PG1 is already selected.
|
||||
// If any of the transactions fails function returns false.
|
||||
// Transmit PWM registers in 12 transfers of 16 bytes.
|
||||
@ -100,69 +100,72 @@ bool CKLED2001_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void CKLED2001_init(uint8_t addr) {
|
||||
void ckled2001_init(uint8_t addr) {
|
||||
// Select to function page
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
|
||||
// Setting LED driver to shutdown mode
|
||||
CKLED2001_write_register(addr, CONFIGURATION_REG, MSKSW_SHUT_DOWN_MODE);
|
||||
ckled2001_write_register(addr, CONFIGURATION_REG, MSKSW_SHUT_DOWN_MODE);
|
||||
// Setting internal channel pulldown/pullup
|
||||
CKLED2001_write_register(addr, PDU_REG, MSKSET_CA_CB_CHANNEL);
|
||||
ckled2001_write_register(addr, PDU_REG, MSKSET_CA_CB_CHANNEL);
|
||||
// Select number of scan phase
|
||||
CKLED2001_write_register(addr, SCAN_PHASE_REG, PHASE_CHANNEL);
|
||||
ckled2001_write_register(addr, SCAN_PHASE_REG, PHASE_CHANNEL);
|
||||
// Setting PWM Delay Phase
|
||||
CKLED2001_write_register(addr, SLEW_RATE_CONTROL_MODE1_REG, MSKPWM_DELAY_PHASE_ENABLE);
|
||||
ckled2001_write_register(addr, SLEW_RATE_CONTROL_MODE1_REG, MSKPWM_DELAY_PHASE_ENABLE);
|
||||
// Setting Driving/Sinking Channel Slew Rate
|
||||
CKLED2001_write_register(addr, SLEW_RATE_CONTROL_MODE2_REG, MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_ENABLE);
|
||||
ckled2001_write_register(addr, SLEW_RATE_CONTROL_MODE2_REG, MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_ENABLE);
|
||||
// Setting Iref
|
||||
CKLED2001_write_register(addr, SOFTWARE_SLEEP_REG, MSKSLEEP_DISABLE);
|
||||
ckled2001_write_register(addr, SOFTWARE_SLEEP_REG, MSKSLEEP_DISABLE);
|
||||
// Set LED CONTROL PAGE (Page 0)
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
|
||||
for (int i = 0; i < LED_CONTROL_ON_OFF_LENGTH; i++) {
|
||||
CKLED2001_write_register(addr, i, 0x00);
|
||||
ckled2001_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// Set PWM PAGE (Page 1)
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_PWM_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_PWM_PAGE);
|
||||
for (int i = 0; i < LED_CURRENT_TUNE_LENGTH; i++) {
|
||||
CKLED2001_write_register(addr, i, 0x00);
|
||||
ckled2001_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// Set CURRENT PAGE (Page 4)
|
||||
uint8_t current_tuen_reg_list[LED_CURRENT_TUNE_LENGTH] = CKLED2001_CURRENT_TUNE;
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, CURRENT_TUNE_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, CURRENT_TUNE_PAGE);
|
||||
for (int i = 0; i < LED_CURRENT_TUNE_LENGTH; i++) {
|
||||
CKLED2001_write_register(addr, i, current_tuen_reg_list[i]);
|
||||
ckled2001_write_register(addr, i, current_tuen_reg_list[i]);
|
||||
}
|
||||
|
||||
// Enable LEDs ON/OFF
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
|
||||
for (int i = 0; i < LED_CONTROL_ON_OFF_LENGTH; i++) {
|
||||
CKLED2001_write_register(addr, i, 0xFF);
|
||||
ckled2001_write_register(addr, i, 0xFF);
|
||||
}
|
||||
|
||||
// Select to function page
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
|
||||
// Setting LED driver to normal mode
|
||||
CKLED2001_write_register(addr, CONFIGURATION_REG, MSKSW_NORMAL_MODE);
|
||||
ckled2001_write_register(addr, CONFIGURATION_REG, MSKSW_NORMAL_MODE);
|
||||
}
|
||||
|
||||
void CKLED2001_set_value(int index, uint8_t value) {
|
||||
void ckled2001_set_value(int index, uint8_t value) {
|
||||
ckled2001_led led;
|
||||
if (index >= 0 && index < LED_MATRIX_LED_COUNT) {
|
||||
memcpy_P(&led, (&g_ckled2001_leds[index]), sizeof(led));
|
||||
|
||||
if (g_pwm_buffer[led.driver][led.v] == value) {
|
||||
return;
|
||||
}
|
||||
g_pwm_buffer[led.driver][led.v] = value;
|
||||
g_pwm_buffer_update_required[led.driver] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CKLED2001_set_value_all(uint8_t value) {
|
||||
void ckled2001_set_value_all(uint8_t value) {
|
||||
for (int i = 0; i < LED_MATRIX_LED_COUNT; i++) {
|
||||
CKLED2001_set_value(i, value);
|
||||
ckled2001_set_value(i, value);
|
||||
}
|
||||
}
|
||||
|
||||
void CKLED2001_set_led_control_register(uint8_t index, bool value) {
|
||||
void ckled2001_set_led_control_register(uint8_t index, bool value) {
|
||||
ckled2001_led led;
|
||||
memcpy_P(&led, (&g_ckled2001_leds[index]), sizeof(led));
|
||||
|
||||
@ -178,41 +181,41 @@ void CKLED2001_set_led_control_register(uint8_t index, bool value) {
|
||||
g_led_control_registers_update_required[led.driver] = true;
|
||||
}
|
||||
|
||||
void CKLED2001_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||
void ckled2001_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||
if (g_pwm_buffer_update_required[index]) {
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_PWM_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_PWM_PAGE);
|
||||
|
||||
// If any of the transactions fail we risk writing dirty PG0,
|
||||
// refresh page 0 just in case.
|
||||
if (!CKLED2001_write_pwm_buffer(addr, g_pwm_buffer[index])) {
|
||||
if (!ckled2001_write_pwm_buffer(addr, g_pwm_buffer[index])) {
|
||||
g_led_control_registers_update_required[index] = true;
|
||||
}
|
||||
}
|
||||
g_pwm_buffer_update_required[index] = false;
|
||||
}
|
||||
|
||||
void CKLED2001_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
void ckled2001_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
if (g_led_control_registers_update_required[index]) {
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
|
||||
for (int i = 0; i < 24; i++) {
|
||||
CKLED2001_write_register(addr, i, g_led_control_registers[index][i]);
|
||||
ckled2001_write_register(addr, i, g_led_control_registers[index][i]);
|
||||
}
|
||||
}
|
||||
g_led_control_registers_update_required[index] = false;
|
||||
}
|
||||
|
||||
void CKLED2001_sw_return_normal(uint8_t addr) {
|
||||
void ckled2001_sw_return_normal(uint8_t addr) {
|
||||
// Select to function page
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
|
||||
// Setting LED driver to normal mode
|
||||
CKLED2001_write_register(addr, CONFIGURATION_REG, MSKSW_NORMAL_MODE);
|
||||
ckled2001_write_register(addr, CONFIGURATION_REG, MSKSW_NORMAL_MODE);
|
||||
}
|
||||
|
||||
void CKLED2001_sw_shutdown(uint8_t addr) {
|
||||
void ckled2001_sw_shutdown(uint8_t addr) {
|
||||
// Select to function page
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
|
||||
// Setting LED driver to shutdown mode
|
||||
CKLED2001_write_register(addr, CONFIGURATION_REG, MSKSW_SHUT_DOWN_MODE);
|
||||
ckled2001_write_register(addr, CONFIGURATION_REG, MSKSW_SHUT_DOWN_MODE);
|
||||
// Write SW Sleep Register
|
||||
CKLED2001_write_register(addr, SOFTWARE_SLEEP_REG, MSKSLEEP_ENABLE);
|
||||
ckled2001_write_register(addr, SOFTWARE_SLEEP_REG, MSKSLEEP_ENABLE);
|
||||
}
|
||||
|
@ -27,24 +27,24 @@ typedef struct ckled2001_led {
|
||||
|
||||
extern const ckled2001_led PROGMEM g_ckled2001_leds[LED_MATRIX_LED_COUNT];
|
||||
|
||||
void CKLED2001_init(uint8_t addr);
|
||||
bool CKLED2001_write_register(uint8_t addr, uint8_t reg, uint8_t data);
|
||||
bool CKLED2001_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
|
||||
void ckled2001_init(uint8_t addr);
|
||||
bool ckled2001_write_register(uint8_t addr, uint8_t reg, uint8_t data);
|
||||
bool ckled2001_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
|
||||
|
||||
void CKLED2001_set_value(int index, uint8_t value);
|
||||
void CKLED2001_set_value_all(uint8_t value);
|
||||
void ckled2001_set_value(int index, uint8_t value);
|
||||
void ckled2001_set_value_all(uint8_t value);
|
||||
|
||||
void CKLED2001_set_led_control_register(uint8_t index, bool value);
|
||||
void ckled2001_set_led_control_register(uint8_t index, bool value);
|
||||
|
||||
// This should not be called from an interrupt
|
||||
// (eg. from a timer interrupt).
|
||||
// Call this while idle (in between matrix scans).
|
||||
// If the buffer is dirty, it will update the driver with the buffer.
|
||||
void CKLED2001_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void CKLED2001_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
void ckled2001_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void ckled2001_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
|
||||
void CKLED2001_sw_return_normal(uint8_t addr);
|
||||
void CKLED2001_sw_shutdown(uint8_t addr);
|
||||
void ckled2001_sw_return_normal(uint8_t addr);
|
||||
void ckled2001_sw_shutdown(uint8_t addr);
|
||||
|
||||
// Registers Page Define
|
||||
#define CONFIGURE_CMD_PAGE 0xFD
|
||||
|
@ -42,7 +42,7 @@ uint8_t g_twi_transfer_buffer[65];
|
||||
// The control buffers match the PG0 LED On/Off registers.
|
||||
// Storing them like this is optimal for I2C transfers to the registers.
|
||||
// We could optimize this and take out the unused registers from these
|
||||
// buffers and the transfers in CKLED2001_write_pwm_buffer() but it's
|
||||
// buffers and the transfers in ckled2001_write_pwm_buffer() but it's
|
||||
// probably not worth the extra complexity.
|
||||
uint8_t g_pwm_buffer[DRIVER_COUNT][192];
|
||||
bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
|
||||
@ -50,7 +50,7 @@ bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
|
||||
uint8_t g_led_control_registers[DRIVER_COUNT][24] = {0};
|
||||
bool g_led_control_registers_update_required[DRIVER_COUNT] = {false};
|
||||
|
||||
bool CKLED2001_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
bool ckled2001_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
// If the transaction fails function returns false.
|
||||
g_twi_transfer_buffer[0] = reg;
|
||||
g_twi_transfer_buffer[1] = data;
|
||||
@ -69,7 +69,7 @@ bool CKLED2001_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKLED2001_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
bool ckled2001_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
// Assumes PG1 is already selected.
|
||||
// If any of the transactions fails function returns false.
|
||||
// Transmit PWM registers in 3 transfers of 64 bytes.
|
||||
@ -99,57 +99,60 @@ bool CKLED2001_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void CKLED2001_init(uint8_t addr) {
|
||||
void ckled2001_init(uint8_t addr) {
|
||||
// Select to function page
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
|
||||
// Setting LED driver to shutdown mode
|
||||
CKLED2001_write_register(addr, CONFIGURATION_REG, MSKSW_SHUT_DOWN_MODE);
|
||||
ckled2001_write_register(addr, CONFIGURATION_REG, MSKSW_SHUT_DOWN_MODE);
|
||||
// Setting internal channel pulldown/pullup
|
||||
CKLED2001_write_register(addr, PDU_REG, MSKSET_CA_CB_CHANNEL);
|
||||
ckled2001_write_register(addr, PDU_REG, MSKSET_CA_CB_CHANNEL);
|
||||
// Select number of scan phase
|
||||
CKLED2001_write_register(addr, SCAN_PHASE_REG, PHASE_CHANNEL);
|
||||
ckled2001_write_register(addr, SCAN_PHASE_REG, PHASE_CHANNEL);
|
||||
// Setting PWM Delay Phase
|
||||
CKLED2001_write_register(addr, SLEW_RATE_CONTROL_MODE1_REG, MSKPWM_DELAY_PHASE_ENABLE);
|
||||
ckled2001_write_register(addr, SLEW_RATE_CONTROL_MODE1_REG, MSKPWM_DELAY_PHASE_ENABLE);
|
||||
// Setting Driving/Sinking Channel Slew Rate
|
||||
CKLED2001_write_register(addr, SLEW_RATE_CONTROL_MODE2_REG, MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_ENABLE);
|
||||
ckled2001_write_register(addr, SLEW_RATE_CONTROL_MODE2_REG, MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_ENABLE);
|
||||
// Setting Iref
|
||||
CKLED2001_write_register(addr, SOFTWARE_SLEEP_REG, MSKSLEEP_DISABLE);
|
||||
ckled2001_write_register(addr, SOFTWARE_SLEEP_REG, MSKSLEEP_DISABLE);
|
||||
// Set LED CONTROL PAGE (Page 0)
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
|
||||
for (int i = 0; i < LED_CONTROL_ON_OFF_LENGTH; i++) {
|
||||
CKLED2001_write_register(addr, i, 0x00);
|
||||
ckled2001_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// Set PWM PAGE (Page 1)
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_PWM_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_PWM_PAGE);
|
||||
for (int i = 0; i < LED_CURRENT_TUNE_LENGTH; i++) {
|
||||
CKLED2001_write_register(addr, i, 0x00);
|
||||
ckled2001_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// Set CURRENT PAGE (Page 4)
|
||||
uint8_t current_tuen_reg_list[LED_CURRENT_TUNE_LENGTH] = CKLED2001_CURRENT_TUNE;
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, CURRENT_TUNE_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, CURRENT_TUNE_PAGE);
|
||||
for (int i = 0; i < LED_CURRENT_TUNE_LENGTH; i++) {
|
||||
CKLED2001_write_register(addr, i, current_tuen_reg_list[i]);
|
||||
ckled2001_write_register(addr, i, current_tuen_reg_list[i]);
|
||||
}
|
||||
|
||||
// Enable LEDs ON/OFF
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
|
||||
for (int i = 0; i < LED_CONTROL_ON_OFF_LENGTH; i++) {
|
||||
CKLED2001_write_register(addr, i, 0xFF);
|
||||
ckled2001_write_register(addr, i, 0xFF);
|
||||
}
|
||||
|
||||
// Select to function page
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
|
||||
// Setting LED driver to normal mode
|
||||
CKLED2001_write_register(addr, CONFIGURATION_REG, MSKSW_NORMAL_MODE);
|
||||
ckled2001_write_register(addr, CONFIGURATION_REG, MSKSW_NORMAL_MODE);
|
||||
}
|
||||
|
||||
void CKLED2001_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
void ckled2001_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
ckled2001_led led;
|
||||
if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
|
||||
memcpy_P(&led, (&g_ckled2001_leds[index]), sizeof(led));
|
||||
|
||||
if (g_pwm_buffer[led.driver][led.r] == red && g_pwm_buffer[led.driver][led.g] == green && g_pwm_buffer[led.driver][led.b] == blue) {
|
||||
return;
|
||||
}
|
||||
g_pwm_buffer[led.driver][led.r] = red;
|
||||
g_pwm_buffer[led.driver][led.g] = green;
|
||||
g_pwm_buffer[led.driver][led.b] = blue;
|
||||
@ -157,13 +160,13 @@ void CKLED2001_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
}
|
||||
}
|
||||
|
||||
void CKLED2001_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
void ckled2001_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
for (int i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
|
||||
CKLED2001_set_color(i, red, green, blue);
|
||||
ckled2001_set_color(i, red, green, blue);
|
||||
}
|
||||
}
|
||||
|
||||
void CKLED2001_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
|
||||
void ckled2001_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
|
||||
ckled2001_led led;
|
||||
memcpy_P(&led, (&g_ckled2001_leds[index]), sizeof(led));
|
||||
|
||||
@ -193,41 +196,41 @@ void CKLED2001_set_led_control_register(uint8_t index, bool red, bool green, boo
|
||||
g_led_control_registers_update_required[led.driver] = true;
|
||||
}
|
||||
|
||||
void CKLED2001_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||
void ckled2001_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||
if (g_pwm_buffer_update_required[index]) {
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_PWM_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_PWM_PAGE);
|
||||
|
||||
// If any of the transactions fail we risk writing dirty PG0,
|
||||
// refresh page 0 just in case.
|
||||
if (!CKLED2001_write_pwm_buffer(addr, g_pwm_buffer[index])) {
|
||||
if (!ckled2001_write_pwm_buffer(addr, g_pwm_buffer[index])) {
|
||||
g_led_control_registers_update_required[index] = true;
|
||||
}
|
||||
}
|
||||
g_pwm_buffer_update_required[index] = false;
|
||||
}
|
||||
|
||||
void CKLED2001_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
void ckled2001_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
if (g_led_control_registers_update_required[index]) {
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, LED_CONTROL_PAGE);
|
||||
for (int i = 0; i < 24; i++) {
|
||||
CKLED2001_write_register(addr, i, g_led_control_registers[index][i]);
|
||||
ckled2001_write_register(addr, i, g_led_control_registers[index][i]);
|
||||
}
|
||||
}
|
||||
g_led_control_registers_update_required[index] = false;
|
||||
}
|
||||
|
||||
void CKLED2001_sw_return_normal(uint8_t addr) {
|
||||
void ckled2001_sw_return_normal(uint8_t addr) {
|
||||
// Select to function page
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
|
||||
// Setting LED driver to normal mode
|
||||
CKLED2001_write_register(addr, CONFIGURATION_REG, MSKSW_NORMAL_MODE);
|
||||
ckled2001_write_register(addr, CONFIGURATION_REG, MSKSW_NORMAL_MODE);
|
||||
}
|
||||
|
||||
void CKLED2001_sw_shutdown(uint8_t addr) {
|
||||
void ckled2001_sw_shutdown(uint8_t addr) {
|
||||
// Select to function page
|
||||
CKLED2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
|
||||
ckled2001_write_register(addr, CONFIGURE_CMD_PAGE, FUNCTION_PAGE);
|
||||
// Setting LED driver to shutdown mode
|
||||
CKLED2001_write_register(addr, CONFIGURATION_REG, MSKSW_SHUT_DOWN_MODE);
|
||||
ckled2001_write_register(addr, CONFIGURATION_REG, MSKSW_SHUT_DOWN_MODE);
|
||||
// Write SW Sleep Register
|
||||
CKLED2001_write_register(addr, SOFTWARE_SLEEP_REG, MSKSLEEP_ENABLE);
|
||||
ckled2001_write_register(addr, SOFTWARE_SLEEP_REG, MSKSLEEP_ENABLE);
|
||||
}
|
||||
|
@ -29,24 +29,24 @@ typedef struct ckled2001_led {
|
||||
|
||||
extern const ckled2001_led PROGMEM g_ckled2001_leds[RGB_MATRIX_LED_COUNT];
|
||||
|
||||
void CKLED2001_init(uint8_t addr);
|
||||
bool CKLED2001_write_register(uint8_t addr, uint8_t reg, uint8_t data);
|
||||
bool CKLED2001_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
|
||||
void ckled2001_init(uint8_t addr);
|
||||
bool ckled2001_write_register(uint8_t addr, uint8_t reg, uint8_t data);
|
||||
bool ckled2001_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
|
||||
|
||||
void CKLED2001_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void CKLED2001_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
|
||||
void ckled2001_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void ckled2001_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
|
||||
|
||||
void CKLED2001_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
|
||||
void ckled2001_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
|
||||
|
||||
// This should not be called from an interrupt
|
||||
// (eg. from a timer interrupt).
|
||||
// Call this while idle (in between matrix scans).
|
||||
// If the buffer is dirty, it will update the driver with the buffer.
|
||||
void CKLED2001_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void CKLED2001_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
void ckled2001_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void ckled2001_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
|
||||
void CKLED2001_sw_return_normal(uint8_t addr);
|
||||
void CKLED2001_sw_shutdown(uint8_t addr);
|
||||
void ckled2001_sw_return_normal(uint8_t addr);
|
||||
void ckled2001_sw_shutdown(uint8_t addr);
|
||||
|
||||
// Registers Page Define
|
||||
#define CONFIGURE_CMD_PAGE 0xFD
|
||||
|
@ -37,58 +37,61 @@ uint8_t g_twi_transfer_buffer[20];
|
||||
uint8_t g_pwm_buffer[18];
|
||||
bool g_pwm_buffer_update_required = false;
|
||||
|
||||
void IS31FL3218_write_register(uint8_t reg, uint8_t data) {
|
||||
void is31fl3218_write_register(uint8_t reg, uint8_t data) {
|
||||
g_twi_transfer_buffer[0] = reg;
|
||||
g_twi_transfer_buffer[1] = data;
|
||||
i2c_transmit(ISSI_ADDRESS, g_twi_transfer_buffer, 2, ISSI_TIMEOUT);
|
||||
}
|
||||
|
||||
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;
|
||||
memcpy(g_twi_transfer_buffer + 1, pwm_buffer, 18);
|
||||
|
||||
i2c_transmit(ISSI_ADDRESS, g_twi_transfer_buffer, 19, ISSI_TIMEOUT);
|
||||
}
|
||||
|
||||
void IS31FL3218_init(void) {
|
||||
void is31fl3218_init(void) {
|
||||
// In case we ever want to reinitialize (?)
|
||||
IS31FL3218_write_register(ISSI_REG_RESET, 0x00);
|
||||
is31fl3218_write_register(ISSI_REG_RESET, 0x00);
|
||||
|
||||
// Turn off software shutdown
|
||||
IS31FL3218_write_register(ISSI_REG_SHUTDOWN, 0x01);
|
||||
is31fl3218_write_register(ISSI_REG_SHUTDOWN, 0x01);
|
||||
|
||||
// Set all PWM values to zero
|
||||
for (uint8_t i = 0; i < 18; i++) {
|
||||
IS31FL3218_write_register(ISSI_REG_PWM + i, 0x00);
|
||||
is31fl3218_write_register(ISSI_REG_PWM + i, 0x00);
|
||||
}
|
||||
|
||||
// Enable all channels
|
||||
for (uint8_t i = 0; i < 3; i++) {
|
||||
IS31FL3218_write_register(ISSI_REG_CONTROL + i, 0b00111111);
|
||||
is31fl3218_write_register(ISSI_REG_CONTROL + i, 0b00111111);
|
||||
}
|
||||
|
||||
// Load PWM registers and LED Control register data
|
||||
IS31FL3218_write_register(ISSI_REG_UPDATE, 0x01);
|
||||
is31fl3218_write_register(ISSI_REG_UPDATE, 0x01);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (g_pwm_buffer[index * 3 + 0] == red && g_pwm_buffer[index * 3 + 1] == green && g_pwm_buffer[index * 3 + 2] == blue) {
|
||||
return;
|
||||
}
|
||||
g_pwm_buffer[index * 3 + 0] = red;
|
||||
g_pwm_buffer[index * 3 + 1] = green;
|
||||
g_pwm_buffer[index * 3 + 2] = blue;
|
||||
g_pwm_buffer_update_required = true;
|
||||
}
|
||||
|
||||
void IS31FL3218_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
void is31fl3218_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
for (int i = 0; i < 6; i++) {
|
||||
IS31FL3218_set_color(i, red, green, blue);
|
||||
is31fl3218_set_color(i, red, green, blue);
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3218_update_pwm_buffers(void) {
|
||||
void is31fl3218_update_pwm_buffers(void) {
|
||||
if (g_pwm_buffer_update_required) {
|
||||
IS31FL3218_write_pwm_buffer(g_pwm_buffer);
|
||||
is31fl3218_write_pwm_buffer(g_pwm_buffer);
|
||||
// Load PWM registers and LED Control register data
|
||||
IS31FL3218_write_register(ISSI_REG_UPDATE, 0x01);
|
||||
is31fl3218_write_register(ISSI_REG_UPDATE, 0x01);
|
||||
}
|
||||
g_pwm_buffer_update_required = false;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
void IS31FL3218_init(void);
|
||||
void IS31FL3218_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void IS31FL3218_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
|
||||
void IS31FL3218_update_pwm_buffers(void);
|
||||
void is31fl3218_init(void);
|
||||
void is31fl3218_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void is31fl3218_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
|
||||
void is31fl3218_update_pwm_buffers(void);
|
||||
|
@ -64,7 +64,7 @@ uint8_t g_twi_transfer_buffer[20];
|
||||
// These buffers match the IS31FL3731 PWM registers 0x24-0xB3.
|
||||
// Storing them like this is optimal for I2C transfers to the registers.
|
||||
// We could optimize this and take out the unused registers from these
|
||||
// buffers and the transfers in IS31FL3731_write_pwm_buffer() but it's
|
||||
// buffers and the transfers in is31fl3731_write_pwm_buffer() but it's
|
||||
// probably not worth the extra complexity.
|
||||
uint8_t g_pwm_buffer[LED_DRIVER_COUNT][144];
|
||||
bool g_pwm_buffer_update_required[LED_DRIVER_COUNT] = {false};
|
||||
@ -95,7 +95,7 @@ bool g_led_control_registers_update_required[LED_DRIVER_COUNT] = {false};
|
||||
// 0x0E - R17,G15,G14,G13,G12,G11,G10,G09
|
||||
// 0x10 - R16,R15,R14,R13,R12,R11,R10,R09
|
||||
|
||||
void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
void is31fl3731_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
g_twi_transfer_buffer[0] = reg;
|
||||
g_twi_transfer_buffer[1] = data;
|
||||
|
||||
@ -110,7 +110,7 @@ void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void IS31FL3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
void is31fl3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
// assumes bank is already selected
|
||||
|
||||
// transmit PWM registers in 9 transfers of 16 bytes
|
||||
@ -135,79 +135,83 @@ void IS31FL3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3731_init(uint8_t addr) {
|
||||
void is31fl3731_init(uint8_t addr) {
|
||||
// In order to avoid the LEDs being driven with garbage data
|
||||
// in the LED driver's PWM registers, first enable software shutdown,
|
||||
// then set up the mode and other settings, clear the PWM registers,
|
||||
// then disable software shutdown.
|
||||
|
||||
// select "function register" bank
|
||||
IS31FL3731_write_register(addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG);
|
||||
is31fl3731_write_register(addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG);
|
||||
|
||||
// enable software shutdown
|
||||
IS31FL3731_write_register(addr, ISSI_REG_SHUTDOWN, 0x00);
|
||||
is31fl3731_write_register(addr, ISSI_REG_SHUTDOWN, 0x00);
|
||||
#ifdef ISSI_3731_DEGHOST // set to enable de-ghosting of the array
|
||||
IS31FL3731_write_register(addr, ISSI_REG_GHOST_IMAGE_PREVENTION, 0x10);
|
||||
is31fl3731_write_register(addr, ISSI_REG_GHOST_IMAGE_PREVENTION, 0x10);
|
||||
#endif
|
||||
|
||||
// this delay was copied from other drivers, might not be needed
|
||||
wait_ms(10);
|
||||
|
||||
// picture mode
|
||||
IS31FL3731_write_register(addr, ISSI_REG_CONFIG, ISSI_REG_CONFIG_PICTUREMODE);
|
||||
is31fl3731_write_register(addr, ISSI_REG_CONFIG, ISSI_REG_CONFIG_PICTUREMODE);
|
||||
// display frame 0
|
||||
IS31FL3731_write_register(addr, ISSI_REG_PICTUREFRAME, 0x00);
|
||||
is31fl3731_write_register(addr, ISSI_REG_PICTUREFRAME, 0x00);
|
||||
// audio sync off
|
||||
IS31FL3731_write_register(addr, ISSI_REG_AUDIOSYNC, 0x00);
|
||||
is31fl3731_write_register(addr, ISSI_REG_AUDIOSYNC, 0x00);
|
||||
|
||||
// select bank 0
|
||||
IS31FL3731_write_register(addr, ISSI_COMMANDREGISTER, 0);
|
||||
is31fl3731_write_register(addr, ISSI_COMMANDREGISTER, 0);
|
||||
|
||||
// turn off all LEDs in the LED control register
|
||||
for (int i = 0x00; i <= 0x11; i++) {
|
||||
IS31FL3731_write_register(addr, i, 0x00);
|
||||
is31fl3731_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// turn off all LEDs in the blink control register (not really needed)
|
||||
for (int i = 0x12; i <= 0x23; i++) {
|
||||
IS31FL3731_write_register(addr, i, 0x00);
|
||||
is31fl3731_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// set PWM on all LEDs to 0
|
||||
for (int i = 0x24; i <= 0xB3; i++) {
|
||||
IS31FL3731_write_register(addr, i, 0x00);
|
||||
is31fl3731_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// select "function register" bank
|
||||
IS31FL3731_write_register(addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG);
|
||||
is31fl3731_write_register(addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG);
|
||||
|
||||
// disable software shutdown
|
||||
IS31FL3731_write_register(addr, ISSI_REG_SHUTDOWN, 0x01);
|
||||
is31fl3731_write_register(addr, ISSI_REG_SHUTDOWN, 0x01);
|
||||
|
||||
// select bank 0 and leave it selected.
|
||||
// most usage after initialization is just writing PWM buffers in bank 0
|
||||
// as there's not much point in double-buffering
|
||||
IS31FL3731_write_register(addr, ISSI_COMMANDREGISTER, 0);
|
||||
is31fl3731_write_register(addr, ISSI_COMMANDREGISTER, 0);
|
||||
}
|
||||
|
||||
void IS31FL3731_set_value(int index, uint8_t value) {
|
||||
void is31fl3731_set_value(int index, uint8_t value) {
|
||||
is31_led led;
|
||||
if (index >= 0 && index < LED_MATRIX_LED_COUNT) {
|
||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||
|
||||
// Subtract 0x24 to get the second index of g_pwm_buffer
|
||||
|
||||
if (g_pwm_buffer[led.driver][led.v - 0x24] == value) {
|
||||
return;
|
||||
}
|
||||
g_pwm_buffer[led.driver][led.v - 0x24] = value;
|
||||
g_pwm_buffer_update_required[led.driver] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3731_set_value_all(uint8_t value) {
|
||||
void is31fl3731_set_value_all(uint8_t value) {
|
||||
for (int i = 0; i < LED_MATRIX_LED_COUNT; i++) {
|
||||
IS31FL3731_set_value(i, value);
|
||||
is31fl3731_set_value(i, value);
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3731_set_led_control_register(uint8_t index, bool value) {
|
||||
void is31fl3731_set_led_control_register(uint8_t index, bool value) {
|
||||
is31_led led;
|
||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||
|
||||
@ -223,17 +227,17 @@ void IS31FL3731_set_led_control_register(uint8_t index, bool value) {
|
||||
g_led_control_registers_update_required[led.driver] = true;
|
||||
}
|
||||
|
||||
void IS31FL3731_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||
void is31fl3731_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||
if (g_pwm_buffer_update_required[index]) {
|
||||
IS31FL3731_write_pwm_buffer(addr, g_pwm_buffer[index]);
|
||||
is31fl3731_write_pwm_buffer(addr, g_pwm_buffer[index]);
|
||||
g_pwm_buffer_update_required[index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3731_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
void is31fl3731_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
if (g_led_control_registers_update_required[index]) {
|
||||
for (int i = 0; i < 18; i++) {
|
||||
IS31FL3731_write_register(addr, i, g_led_control_registers[index][i]);
|
||||
is31fl3731_write_register(addr, i, g_led_control_registers[index][i]);
|
||||
}
|
||||
g_led_control_registers_update_required[index] = false;
|
||||
}
|
||||
|
@ -30,21 +30,21 @@ typedef struct is31_led {
|
||||
|
||||
extern const is31_led PROGMEM g_is31_leds[LED_MATRIX_LED_COUNT];
|
||||
|
||||
void IS31FL3731_init(uint8_t addr);
|
||||
void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data);
|
||||
void IS31FL3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
|
||||
void is31fl3731_init(uint8_t addr);
|
||||
void is31fl3731_write_register(uint8_t addr, uint8_t reg, uint8_t data);
|
||||
void is31fl3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
|
||||
|
||||
void IS31FL3731_set_value(int index, uint8_t value);
|
||||
void IS31FL3731_set_value_all(uint8_t value);
|
||||
void is31fl3731_set_value(int index, uint8_t value);
|
||||
void is31fl3731_set_value_all(uint8_t value);
|
||||
|
||||
void IS31FL3731_set_led_control_register(uint8_t index, bool value);
|
||||
void is31fl3731_set_led_control_register(uint8_t index, bool value);
|
||||
|
||||
// This should not be called from an interrupt
|
||||
// (eg. from a timer interrupt).
|
||||
// Call this while idle (in between matrix scans).
|
||||
// If the buffer is dirty, it will update the driver with the buffer.
|
||||
void IS31FL3731_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void IS31FL3731_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
void is31fl3731_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void is31fl3731_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
|
||||
#define C1_1 0x24
|
||||
#define C1_2 0x25
|
||||
|
@ -63,7 +63,7 @@ uint8_t g_twi_transfer_buffer[20];
|
||||
// These buffers match the IS31FL3731 PWM registers 0x24-0xB3.
|
||||
// Storing them like this is optimal for I2C transfers to the registers.
|
||||
// We could optimize this and take out the unused registers from these
|
||||
// buffers and the transfers in IS31FL3731_write_pwm_buffer() but it's
|
||||
// buffers and the transfers in is31fl3731_write_pwm_buffer() but it's
|
||||
// probably not worth the extra complexity.
|
||||
uint8_t g_pwm_buffer[DRIVER_COUNT][144];
|
||||
bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
|
||||
@ -85,7 +85,7 @@ bool g_led_control_registers_update_required[DRIVER_COUNT] = {false};
|
||||
// 0x0E - R17,G15,G14,G13,G12,G11,G10,G09
|
||||
// 0x10 - R16,R15,R14,R13,R12,R11,R10,R09
|
||||
|
||||
void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
void is31fl3731_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
g_twi_transfer_buffer[0] = reg;
|
||||
g_twi_transfer_buffer[1] = data;
|
||||
|
||||
@ -98,7 +98,7 @@ void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void IS31FL3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
void is31fl3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
// assumes bank is already selected
|
||||
|
||||
// transmit PWM registers in 9 transfers of 16 bytes
|
||||
@ -123,67 +123,70 @@ void IS31FL3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3731_init(uint8_t addr) {
|
||||
void is31fl3731_init(uint8_t addr) {
|
||||
// In order to avoid the LEDs being driven with garbage data
|
||||
// in the LED driver's PWM registers, first enable software shutdown,
|
||||
// then set up the mode and other settings, clear the PWM registers,
|
||||
// then disable software shutdown.
|
||||
|
||||
// select "function register" bank
|
||||
IS31FL3731_write_register(addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG);
|
||||
is31fl3731_write_register(addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG);
|
||||
|
||||
// enable software shutdown
|
||||
IS31FL3731_write_register(addr, ISSI_REG_SHUTDOWN, 0x00);
|
||||
is31fl3731_write_register(addr, ISSI_REG_SHUTDOWN, 0x00);
|
||||
#ifdef ISSI_3731_DEGHOST // set to enable de-ghosting of the array
|
||||
IS31FL3731_write_register(addr, ISSI_REG_GHOST_IMAGE_PREVENTION, 0x10);
|
||||
is31fl3731_write_register(addr, ISSI_REG_GHOST_IMAGE_PREVENTION, 0x10);
|
||||
#endif
|
||||
|
||||
// this delay was copied from other drivers, might not be needed
|
||||
wait_ms(10);
|
||||
|
||||
// picture mode
|
||||
IS31FL3731_write_register(addr, ISSI_REG_CONFIG, ISSI_REG_CONFIG_PICTUREMODE);
|
||||
is31fl3731_write_register(addr, ISSI_REG_CONFIG, ISSI_REG_CONFIG_PICTUREMODE);
|
||||
// display frame 0
|
||||
IS31FL3731_write_register(addr, ISSI_REG_PICTUREFRAME, 0x00);
|
||||
is31fl3731_write_register(addr, ISSI_REG_PICTUREFRAME, 0x00);
|
||||
// audio sync off
|
||||
IS31FL3731_write_register(addr, ISSI_REG_AUDIOSYNC, 0x00);
|
||||
is31fl3731_write_register(addr, ISSI_REG_AUDIOSYNC, 0x00);
|
||||
|
||||
// select bank 0
|
||||
IS31FL3731_write_register(addr, ISSI_COMMANDREGISTER, 0);
|
||||
is31fl3731_write_register(addr, ISSI_COMMANDREGISTER, 0);
|
||||
|
||||
// turn off all LEDs in the LED control register
|
||||
for (int i = 0x00; i <= 0x11; i++) {
|
||||
IS31FL3731_write_register(addr, i, 0x00);
|
||||
is31fl3731_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// turn off all LEDs in the blink control register (not really needed)
|
||||
for (int i = 0x12; i <= 0x23; i++) {
|
||||
IS31FL3731_write_register(addr, i, 0x00);
|
||||
is31fl3731_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// set PWM on all LEDs to 0
|
||||
for (int i = 0x24; i <= 0xB3; i++) {
|
||||
IS31FL3731_write_register(addr, i, 0x00);
|
||||
is31fl3731_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// select "function register" bank
|
||||
IS31FL3731_write_register(addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG);
|
||||
is31fl3731_write_register(addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG);
|
||||
|
||||
// disable software shutdown
|
||||
IS31FL3731_write_register(addr, ISSI_REG_SHUTDOWN, 0x01);
|
||||
is31fl3731_write_register(addr, ISSI_REG_SHUTDOWN, 0x01);
|
||||
|
||||
// select bank 0 and leave it selected.
|
||||
// most usage after initialization is just writing PWM buffers in bank 0
|
||||
// as there's not much point in double-buffering
|
||||
IS31FL3731_write_register(addr, ISSI_COMMANDREGISTER, 0);
|
||||
is31fl3731_write_register(addr, ISSI_COMMANDREGISTER, 0);
|
||||
}
|
||||
|
||||
void IS31FL3731_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
void is31fl3731_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
is31_led led;
|
||||
if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
|
||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||
|
||||
// Subtract 0x24 to get the second index of g_pwm_buffer
|
||||
if (g_pwm_buffer[led.driver][led.r - 0x24] == red && g_pwm_buffer[led.driver][led.g - 0x24] == green && g_pwm_buffer[led.driver][led.b - 0x24] == blue) {
|
||||
return;
|
||||
}
|
||||
g_pwm_buffer[led.driver][led.r - 0x24] = red;
|
||||
g_pwm_buffer[led.driver][led.g - 0x24] = green;
|
||||
g_pwm_buffer[led.driver][led.b - 0x24] = blue;
|
||||
@ -191,13 +194,13 @@ void IS31FL3731_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3731_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
void is31fl3731_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
for (int i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
|
||||
IS31FL3731_set_color(i, red, green, blue);
|
||||
is31fl3731_set_color(i, red, green, blue);
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3731_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
|
||||
void is31fl3731_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
|
||||
is31_led led;
|
||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||
|
||||
@ -227,17 +230,17 @@ void IS31FL3731_set_led_control_register(uint8_t index, bool red, bool green, bo
|
||||
g_led_control_registers_update_required[led.driver] = true;
|
||||
}
|
||||
|
||||
void IS31FL3731_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||
void is31fl3731_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||
if (g_pwm_buffer_update_required[index]) {
|
||||
IS31FL3731_write_pwm_buffer(addr, g_pwm_buffer[index]);
|
||||
is31fl3731_write_pwm_buffer(addr, g_pwm_buffer[index]);
|
||||
}
|
||||
g_pwm_buffer_update_required[index] = false;
|
||||
}
|
||||
|
||||
void IS31FL3731_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
void is31fl3731_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
if (g_led_control_registers_update_required[index]) {
|
||||
for (int i = 0; i < 18; i++) {
|
||||
IS31FL3731_write_register(addr, i, g_led_control_registers[index][i]);
|
||||
is31fl3731_write_register(addr, i, g_led_control_registers[index][i]);
|
||||
}
|
||||
}
|
||||
g_led_control_registers_update_required[index] = false;
|
||||
|
@ -31,21 +31,21 @@ typedef struct is31_led {
|
||||
|
||||
extern const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT];
|
||||
|
||||
void IS31FL3731_init(uint8_t addr);
|
||||
void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data);
|
||||
void IS31FL3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
|
||||
void is31fl3731_init(uint8_t addr);
|
||||
void is31fl3731_write_register(uint8_t addr, uint8_t reg, uint8_t data);
|
||||
void is31fl3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
|
||||
|
||||
void IS31FL3731_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void IS31FL3731_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
|
||||
void is31fl3731_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void is31fl3731_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
|
||||
|
||||
void IS31FL3731_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
|
||||
void is31fl3731_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
|
||||
|
||||
// This should not be called from an interrupt
|
||||
// (eg. from a timer interrupt).
|
||||
// Call this while idle (in between matrix scans).
|
||||
// If the buffer is dirty, it will update the driver with the buffer.
|
||||
void IS31FL3731_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void IS31FL3731_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
void is31fl3731_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void is31fl3731_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
|
||||
#define C1_1 0x24
|
||||
#define C1_2 0x25
|
||||
|
@ -81,7 +81,7 @@ uint8_t g_twi_transfer_buffer[20];
|
||||
// The control buffers match the PG0 LED On/Off registers.
|
||||
// Storing them like this is optimal for I2C transfers to the registers.
|
||||
// We could optimize this and take out the unused registers from these
|
||||
// buffers and the transfers in IS31FL3733_write_pwm_buffer() but it's
|
||||
// buffers and the transfers in is31fl3733_write_pwm_buffer() but it's
|
||||
// probably not worth the extra complexity.
|
||||
uint8_t g_pwm_buffer[LED_DRIVER_COUNT][192];
|
||||
bool g_pwm_buffer_update_required[LED_DRIVER_COUNT] = {false};
|
||||
@ -98,7 +98,7 @@ uint8_t g_led_control_registers[LED_DRIVER_COUNT][24] = {{0}, {0}, {0}, {0}};
|
||||
#endif
|
||||
bool g_led_control_registers_update_required[LED_DRIVER_COUNT] = {false};
|
||||
|
||||
bool IS31FL3733_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
bool is31fl3733_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
// If the transaction fails function returns false.
|
||||
g_twi_transfer_buffer[0] = reg;
|
||||
g_twi_transfer_buffer[1] = data;
|
||||
@ -117,7 +117,7 @@ bool IS31FL3733_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IS31FL3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
bool is31fl3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
// Assumes PG1 is already selected.
|
||||
// If any of the transactions fails function returns false.
|
||||
// Transmit PWM registers in 12 transfers of 16 bytes.
|
||||
@ -146,7 +146,7 @@ bool IS31FL3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void IS31FL3733_init(uint8_t addr, uint8_t sync) {
|
||||
void is31fl3733_init(uint8_t addr, uint8_t sync) {
|
||||
// In order to avoid the LEDs being driven with garbage data
|
||||
// in the LED driver's PWM registers, shutdown is enabled last.
|
||||
// Set up the mode and other settings, clear the PWM registers,
|
||||
@ -154,61 +154,66 @@ void IS31FL3733_init(uint8_t addr, uint8_t sync) {
|
||||
// Sync is passed so set it according to the datasheet.
|
||||
|
||||
// Unlock the command register.
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
|
||||
// Select PG0
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
|
||||
// Turn off all LEDs.
|
||||
for (int i = 0x00; i <= 0x17; i++) {
|
||||
IS31FL3733_write_register(addr, i, 0x00);
|
||||
is31fl3733_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// Unlock the command register.
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
|
||||
// Select PG1
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
|
||||
// Set PWM on all LEDs to 0
|
||||
// No need to setup Breath registers to PWM as that is the default.
|
||||
for (int i = 0x00; i <= 0xBF; i++) {
|
||||
IS31FL3733_write_register(addr, i, 0x00);
|
||||
is31fl3733_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// Unlock the command register.
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
|
||||
// Select PG3
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
|
||||
// Set de-ghost pull-up resistors (SWx)
|
||||
IS31FL3733_write_register(addr, ISSI_REG_SWPULLUP, ISSI_SWPULLUP);
|
||||
is31fl3733_write_register(addr, ISSI_REG_SWPULLUP, ISSI_SWPULLUP);
|
||||
// Set de-ghost pull-down resistors (CSx)
|
||||
IS31FL3733_write_register(addr, ISSI_REG_CSPULLUP, ISSI_CSPULLUP);
|
||||
is31fl3733_write_register(addr, ISSI_REG_CSPULLUP, ISSI_CSPULLUP);
|
||||
// Set global current to maximum.
|
||||
IS31FL3733_write_register(addr, ISSI_REG_GLOBALCURRENT, ISSI_GLOBALCURRENT);
|
||||
is31fl3733_write_register(addr, ISSI_REG_GLOBALCURRENT, ISSI_GLOBALCURRENT);
|
||||
// Disable software shutdown.
|
||||
IS31FL3733_write_register(addr, ISSI_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((ISSI_PWM_FREQUENCY & 0b111) << 3) | 0x01);
|
||||
is31fl3733_write_register(addr, ISSI_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((ISSI_PWM_FREQUENCY & 0b111) << 3) | 0x01);
|
||||
|
||||
// Wait 10ms to ensure the device has woken up.
|
||||
wait_ms(10);
|
||||
}
|
||||
|
||||
void IS31FL3733_set_value(int index, uint8_t value) {
|
||||
void is31fl3733_set_value(int index, uint8_t value) {
|
||||
is31_led led;
|
||||
if (index >= 0 && index < LED_MATRIX_LED_COUNT) {
|
||||
is31_led led = g_is31_leds[index];
|
||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||
|
||||
if (g_pwm_buffer[led.driver][led.v] == value) {
|
||||
return;
|
||||
}
|
||||
g_pwm_buffer[led.driver][led.v] = value;
|
||||
g_pwm_buffer_update_required[led.driver] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3733_set_value_all(uint8_t value) {
|
||||
void is31fl3733_set_value_all(uint8_t value) {
|
||||
for (int i = 0; i < LED_MATRIX_LED_COUNT; i++) {
|
||||
IS31FL3733_set_value(i, value);
|
||||
is31fl3733_set_value(i, value);
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3733_set_led_control_register(uint8_t index, bool value) {
|
||||
is31_led led = g_is31_leds[index];
|
||||
void is31fl3733_set_led_control_register(uint8_t index, bool value) {
|
||||
is31_led led;
|
||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||
|
||||
uint8_t control_register = led.v / 8;
|
||||
uint8_t bit_value = led.v % 8;
|
||||
@ -222,28 +227,28 @@ void IS31FL3733_set_led_control_register(uint8_t index, bool value) {
|
||||
g_led_control_registers_update_required[led.driver] = true;
|
||||
}
|
||||
|
||||
void IS31FL3733_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||
void is31fl3733_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||
if (g_pwm_buffer_update_required[index]) {
|
||||
// Firstly we need to unlock the command register and select PG1.
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
|
||||
|
||||
// If any of the transactions fail we risk writing dirty PG0,
|
||||
// refresh page 0 just in case.
|
||||
if (!IS31FL3733_write_pwm_buffer(addr, g_pwm_buffer[index])) {
|
||||
if (!is31fl3733_write_pwm_buffer(addr, g_pwm_buffer[index])) {
|
||||
g_led_control_registers_update_required[index] = true;
|
||||
}
|
||||
g_pwm_buffer_update_required[index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3733_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
void is31fl3733_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
if (g_led_control_registers_update_required[index]) {
|
||||
// Firstly we need to unlock the command register and select PG0
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
|
||||
for (int i = 0; i < 24; i++) {
|
||||
IS31FL3733_write_register(addr, i, g_led_control_registers[index][i]);
|
||||
is31fl3733_write_register(addr, i, g_led_control_registers[index][i]);
|
||||
}
|
||||
g_led_control_registers_update_required[index] = false;
|
||||
}
|
||||
|
@ -30,23 +30,23 @@ typedef struct is31_led {
|
||||
uint8_t v;
|
||||
} __attribute__((packed)) is31_led;
|
||||
|
||||
extern const is31_led __flash g_is31_leds[LED_MATRIX_LED_COUNT];
|
||||
extern const is31_led PROGMEM g_is31_leds[LED_MATRIX_LED_COUNT];
|
||||
|
||||
void IS31FL3733_init(uint8_t addr, uint8_t sync);
|
||||
bool IS31FL3733_write_register(uint8_t addr, uint8_t reg, uint8_t data);
|
||||
bool IS31FL3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
|
||||
void is31fl3733_init(uint8_t addr, uint8_t sync);
|
||||
bool is31fl3733_write_register(uint8_t addr, uint8_t reg, uint8_t data);
|
||||
bool is31fl3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
|
||||
|
||||
void IS31FL3733_set_value(int index, uint8_t value);
|
||||
void IS31FL3733_set_value_all(uint8_t value);
|
||||
void is31fl3733_set_value(int index, uint8_t value);
|
||||
void is31fl3733_set_value_all(uint8_t value);
|
||||
|
||||
void IS31FL3733_set_led_control_register(uint8_t index, bool value);
|
||||
void is31fl3733_set_led_control_register(uint8_t index, bool value);
|
||||
|
||||
// This should not be called from an interrupt
|
||||
// (eg. from a timer interrupt).
|
||||
// Call this while idle (in between matrix scans).
|
||||
// If the buffer is dirty, it will update the driver with the buffer.
|
||||
void IS31FL3733_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void IS31FL3733_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
void is31fl3733_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void is31fl3733_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
|
||||
#define PUR_0R 0x00 // No PUR resistor
|
||||
#define PUR_05KR 0x02 // 0.5k Ohm resistor in t_NOL
|
||||
|
@ -80,7 +80,7 @@ uint8_t g_twi_transfer_buffer[20];
|
||||
// The control buffers match the PG0 LED On/Off registers.
|
||||
// Storing them like this is optimal for I2C transfers to the registers.
|
||||
// We could optimize this and take out the unused registers from these
|
||||
// buffers and the transfers in IS31FL3733_write_pwm_buffer() but it's
|
||||
// buffers and the transfers in is31fl3733_write_pwm_buffer() but it's
|
||||
// probably not worth the extra complexity.
|
||||
uint8_t g_pwm_buffer[DRIVER_COUNT][192];
|
||||
bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
|
||||
@ -88,7 +88,7 @@ bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
|
||||
uint8_t g_led_control_registers[DRIVER_COUNT][24] = {0};
|
||||
bool g_led_control_registers_update_required[DRIVER_COUNT] = {false};
|
||||
|
||||
bool IS31FL3733_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
bool is31fl3733_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
// If the transaction fails function returns false.
|
||||
g_twi_transfer_buffer[0] = reg;
|
||||
g_twi_transfer_buffer[1] = data;
|
||||
@ -107,7 +107,7 @@ bool IS31FL3733_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IS31FL3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
bool is31fl3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
// Assumes PG1 is already selected.
|
||||
// If any of the transactions fails function returns false.
|
||||
// Transmit PWM registers in 12 transfers of 16 bytes.
|
||||
@ -138,7 +138,7 @@ bool IS31FL3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void IS31FL3733_init(uint8_t addr, uint8_t sync) {
|
||||
void is31fl3733_init(uint8_t addr, uint8_t sync) {
|
||||
// In order to avoid the LEDs being driven with garbage data
|
||||
// in the LED driver's PWM registers, shutdown is enabled last.
|
||||
// Set up the mode and other settings, clear the PWM registers,
|
||||
@ -146,49 +146,52 @@ void IS31FL3733_init(uint8_t addr, uint8_t sync) {
|
||||
// Sync is passed so set it according to the datasheet.
|
||||
|
||||
// Unlock the command register.
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
|
||||
// Select PG0
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
|
||||
// Turn off all LEDs.
|
||||
for (int i = 0x00; i <= 0x17; i++) {
|
||||
IS31FL3733_write_register(addr, i, 0x00);
|
||||
is31fl3733_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// Unlock the command register.
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
|
||||
// Select PG1
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
|
||||
// Set PWM on all LEDs to 0
|
||||
// No need to setup Breath registers to PWM as that is the default.
|
||||
for (int i = 0x00; i <= 0xBF; i++) {
|
||||
IS31FL3733_write_register(addr, i, 0x00);
|
||||
is31fl3733_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// Unlock the command register.
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
|
||||
// Select PG3
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
|
||||
// Set de-ghost pull-up resistors (SWx)
|
||||
IS31FL3733_write_register(addr, ISSI_REG_SWPULLUP, ISSI_SWPULLUP);
|
||||
is31fl3733_write_register(addr, ISSI_REG_SWPULLUP, ISSI_SWPULLUP);
|
||||
// Set de-ghost pull-down resistors (CSx)
|
||||
IS31FL3733_write_register(addr, ISSI_REG_CSPULLUP, ISSI_CSPULLUP);
|
||||
is31fl3733_write_register(addr, ISSI_REG_CSPULLUP, ISSI_CSPULLUP);
|
||||
// Set global current to maximum.
|
||||
IS31FL3733_write_register(addr, ISSI_REG_GLOBALCURRENT, ISSI_GLOBALCURRENT);
|
||||
is31fl3733_write_register(addr, ISSI_REG_GLOBALCURRENT, ISSI_GLOBALCURRENT);
|
||||
// Disable software shutdown.
|
||||
IS31FL3733_write_register(addr, ISSI_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((ISSI_PWM_FREQUENCY & 0b111) << 3) | 0x01);
|
||||
is31fl3733_write_register(addr, ISSI_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((ISSI_PWM_FREQUENCY & 0b111) << 3) | 0x01);
|
||||
|
||||
// Wait 10ms to ensure the device has woken up.
|
||||
wait_ms(10);
|
||||
}
|
||||
|
||||
void IS31FL3733_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
void is31fl3733_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
is31_led led;
|
||||
if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
|
||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||
|
||||
if (g_pwm_buffer[led.driver][led.r] == red && g_pwm_buffer[led.driver][led.g] == green && g_pwm_buffer[led.driver][led.b] == blue) {
|
||||
return;
|
||||
}
|
||||
g_pwm_buffer[led.driver][led.r] = red;
|
||||
g_pwm_buffer[led.driver][led.g] = green;
|
||||
g_pwm_buffer[led.driver][led.b] = blue;
|
||||
@ -196,13 +199,13 @@ void IS31FL3733_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3733_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
void is31fl3733_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
for (int i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
|
||||
IS31FL3733_set_color(i, red, green, blue);
|
||||
is31fl3733_set_color(i, red, green, blue);
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3733_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
|
||||
void is31fl3733_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
|
||||
is31_led led;
|
||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||
|
||||
@ -232,28 +235,28 @@ void IS31FL3733_set_led_control_register(uint8_t index, bool red, bool green, bo
|
||||
g_led_control_registers_update_required[led.driver] = true;
|
||||
}
|
||||
|
||||
void IS31FL3733_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||
void is31fl3733_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||
if (g_pwm_buffer_update_required[index]) {
|
||||
// Firstly we need to unlock the command register and select PG1.
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
|
||||
|
||||
// If any of the transactions fail we risk writing dirty PG0,
|
||||
// refresh page 0 just in case.
|
||||
if (!IS31FL3733_write_pwm_buffer(addr, g_pwm_buffer[index])) {
|
||||
if (!is31fl3733_write_pwm_buffer(addr, g_pwm_buffer[index])) {
|
||||
g_led_control_registers_update_required[index] = true;
|
||||
}
|
||||
}
|
||||
g_pwm_buffer_update_required[index] = false;
|
||||
}
|
||||
|
||||
void IS31FL3733_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
void is31fl3733_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
if (g_led_control_registers_update_required[index]) {
|
||||
// Firstly we need to unlock the command register and select PG0
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
IS31FL3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
|
||||
for (int i = 0; i < 24; i++) {
|
||||
IS31FL3733_write_register(addr, i, g_led_control_registers[index][i]);
|
||||
is31fl3733_write_register(addr, i, g_led_control_registers[index][i]);
|
||||
}
|
||||
}
|
||||
g_led_control_registers_update_required[index] = false;
|
||||
|
@ -32,21 +32,21 @@ typedef struct is31_led {
|
||||
|
||||
extern const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT];
|
||||
|
||||
void IS31FL3733_init(uint8_t addr, uint8_t sync);
|
||||
bool IS31FL3733_write_register(uint8_t addr, uint8_t reg, uint8_t data);
|
||||
bool IS31FL3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
|
||||
void is31fl3733_init(uint8_t addr, uint8_t sync);
|
||||
bool is31fl3733_write_register(uint8_t addr, uint8_t reg, uint8_t data);
|
||||
bool is31fl3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
|
||||
|
||||
void IS31FL3733_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void IS31FL3733_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
|
||||
void is31fl3733_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void is31fl3733_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
|
||||
|
||||
void IS31FL3733_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
|
||||
void is31fl3733_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
|
||||
|
||||
// This should not be called from an interrupt
|
||||
// (eg. from a timer interrupt).
|
||||
// Call this while idle (in between matrix scans).
|
||||
// If the buffer is dirty, it will update the driver with the buffer.
|
||||
void IS31FL3733_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void IS31FL3733_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
void is31fl3733_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void is31fl3733_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
|
||||
#define PUR_0R 0x00 // No PUR resistor
|
||||
#define PUR_05KR 0x02 // 0.5k Ohm resistor in t_NOL
|
||||
|
@ -74,7 +74,7 @@ uint8_t g_twi_transfer_buffer[20];
|
||||
// The control buffers match the PG0 LED On/Off registers.
|
||||
// Storing them like this is optimal for I2C transfers to the registers.
|
||||
// We could optimize this and take out the unused registers from these
|
||||
// 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.
|
||||
uint8_t g_pwm_buffer[DRIVER_COUNT][192];
|
||||
bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
|
||||
@ -82,7 +82,7 @@ bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
|
||||
uint8_t g_led_control_registers[DRIVER_COUNT][24] = {{0}, {0}};
|
||||
bool g_led_control_registers_update_required = false;
|
||||
|
||||
void IS31FL3736_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
void is31fl3736_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
g_twi_transfer_buffer[0] = reg;
|
||||
g_twi_transfer_buffer[1] = data;
|
||||
|
||||
@ -95,7 +95,7 @@ void IS31FL3736_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void IS31FL3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
void is31fl3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
// assumes PG1 is already selected
|
||||
|
||||
// transmit PWM registers in 12 transfers of 16 bytes
|
||||
@ -119,56 +119,59 @@ void IS31FL3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3736_init(uint8_t addr) {
|
||||
void is31fl3736_init(uint8_t addr) {
|
||||
// In order to avoid the LEDs being driven with garbage data
|
||||
// in the LED driver's PWM registers, shutdown is enabled last.
|
||||
// Set up the mode and other settings, clear the PWM registers,
|
||||
// then disable software shutdown.
|
||||
|
||||
// Unlock the command register.
|
||||
IS31FL3736_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3736_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
|
||||
// Select PG0
|
||||
IS31FL3736_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
|
||||
is31fl3736_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
|
||||
// Turn off all LEDs.
|
||||
for (int i = 0x00; i <= 0x17; i++) {
|
||||
IS31FL3736_write_register(addr, i, 0x00);
|
||||
is31fl3736_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// Unlock the command register.
|
||||
IS31FL3736_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3736_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
|
||||
// Select PG1
|
||||
IS31FL3736_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
|
||||
is31fl3736_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
|
||||
// Set PWM on all LEDs to 0
|
||||
// No need to setup Breath registers to PWM as that is the default.
|
||||
for (int i = 0x00; i <= 0xBF; i++) {
|
||||
IS31FL3736_write_register(addr, i, 0x00);
|
||||
is31fl3736_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// Unlock the command register.
|
||||
IS31FL3736_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3736_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
|
||||
// Select PG3
|
||||
IS31FL3736_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
|
||||
is31fl3736_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
|
||||
// Set de-ghost pull-up resistors (SWx)
|
||||
IS31FL3736_write_register(addr, ISSI_REG_SWPULLUP, ISSI_SWPULLUP);
|
||||
is31fl3736_write_register(addr, ISSI_REG_SWPULLUP, ISSI_SWPULLUP);
|
||||
// Set de-ghost pull-down resistors (CSx)
|
||||
IS31FL3736_write_register(addr, ISSI_REG_CSPULLUP, ISSI_CSPULLUP);
|
||||
is31fl3736_write_register(addr, ISSI_REG_CSPULLUP, ISSI_CSPULLUP);
|
||||
// Set global current to maximum.
|
||||
IS31FL3736_write_register(addr, ISSI_REG_GLOBALCURRENT, ISSI_GLOBALCURRENT);
|
||||
is31fl3736_write_register(addr, ISSI_REG_GLOBALCURRENT, ISSI_GLOBALCURRENT);
|
||||
// Disable software shutdown.
|
||||
IS31FL3736_write_register(addr, ISSI_REG_CONFIGURATION, 0x01);
|
||||
is31fl3736_write_register(addr, ISSI_REG_CONFIGURATION, 0x01);
|
||||
|
||||
// Wait 10ms to ensure the device has woken up.
|
||||
wait_ms(10);
|
||||
}
|
||||
|
||||
void IS31FL3736_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
void is31fl3736_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
is31_led led;
|
||||
if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
|
||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||
|
||||
if (g_pwm_buffer[led.driver][led.r] == red && g_pwm_buffer[led.driver][led.g] == green && g_pwm_buffer[led.driver][led.b] == blue) {
|
||||
return;
|
||||
}
|
||||
g_pwm_buffer[led.driver][led.r] = red;
|
||||
g_pwm_buffer[led.driver][led.g] = green;
|
||||
g_pwm_buffer[led.driver][led.b] = blue;
|
||||
@ -176,13 +179,13 @@ void IS31FL3736_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3736_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
void is31fl3736_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
for (int i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
|
||||
IS31FL3736_set_color(i, red, green, blue);
|
||||
is31fl3736_set_color(i, red, green, blue);
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3736_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
|
||||
void is31fl3736_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
|
||||
is31_led led;
|
||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||
|
||||
@ -226,7 +229,7 @@ void IS31FL3736_set_led_control_register(uint8_t index, bool red, bool green, bo
|
||||
g_led_control_registers_update_required = true;
|
||||
}
|
||||
|
||||
void IS31FL3736_mono_set_brightness(int index, uint8_t value) {
|
||||
void is31fl3736_mono_set_brightness(int index, uint8_t value) {
|
||||
if (index >= 0 && index < 96) {
|
||||
// Index in range 0..95 -> A1..A8, B1..B8, etc.
|
||||
// Map index 0..95 to registers 0x00..0xBE (interleaved)
|
||||
@ -236,13 +239,13 @@ void IS31FL3736_mono_set_brightness(int index, uint8_t value) {
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3736_mono_set_brightness_all(uint8_t value) {
|
||||
void is31fl3736_mono_set_brightness_all(uint8_t value) {
|
||||
for (int i = 0; i < 96; i++) {
|
||||
IS31FL3736_mono_set_brightness(i, value);
|
||||
is31fl3736_mono_set_brightness(i, value);
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3736_mono_set_led_control_register(uint8_t index, bool enabled) {
|
||||
void is31fl3736_mono_set_led_control_register(uint8_t index, bool enabled) {
|
||||
// Index in range 0..95 -> A1..A8, B1..B8, etc.
|
||||
|
||||
// Map index 0..95 to registers 0x00..0xBE (interleaved)
|
||||
@ -260,25 +263,25 @@ void IS31FL3736_mono_set_led_control_register(uint8_t index, bool enabled) {
|
||||
g_led_control_registers_update_required = true;
|
||||
}
|
||||
|
||||
void IS31FL3736_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||
void is31fl3736_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||
if (g_pwm_buffer_update_required[index]) {
|
||||
// Firstly we need to unlock the command register and select PG1
|
||||
IS31FL3736_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
IS31FL3736_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
|
||||
is31fl3736_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3736_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
|
||||
|
||||
IS31FL3736_write_pwm_buffer(addr, g_pwm_buffer[index]);
|
||||
is31fl3736_write_pwm_buffer(addr, g_pwm_buffer[index]);
|
||||
}
|
||||
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) {
|
||||
if (g_led_control_registers_update_required) {
|
||||
// Firstly we need to unlock the command register and select PG0
|
||||
IS31FL3736_write_register(addr1, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
IS31FL3736_write_register(addr1, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
|
||||
is31fl3736_write_register(addr1, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3736_write_register(addr1, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
|
||||
for (int i = 0; i < 24; i++) {
|
||||
IS31FL3736_write_register(addr1, i, g_led_control_registers[0][i]);
|
||||
// IS31FL3736_write_register(addr2, i, g_led_control_registers[1][i]);
|
||||
is31fl3736_write_register(addr1, i, g_led_control_registers[0][i]);
|
||||
// is31fl3736_write_register(addr2, i, g_led_control_registers[1][i]);
|
||||
}
|
||||
g_led_control_registers_update_required = false;
|
||||
}
|
||||
|
@ -42,25 +42,25 @@ typedef struct is31_led {
|
||||
|
||||
extern const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT];
|
||||
|
||||
void IS31FL3736_init(uint8_t addr);
|
||||
void IS31FL3736_write_register(uint8_t addr, uint8_t reg, uint8_t data);
|
||||
void IS31FL3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
|
||||
void is31fl3736_init(uint8_t addr);
|
||||
void is31fl3736_write_register(uint8_t addr, uint8_t reg, uint8_t data);
|
||||
void is31fl3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
|
||||
|
||||
void IS31FL3736_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void IS31FL3736_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
|
||||
void is31fl3736_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void is31fl3736_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
|
||||
|
||||
void IS31FL3736_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
|
||||
void is31fl3736_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
|
||||
|
||||
void IS31FL3736_mono_set_brightness(int index, uint8_t value);
|
||||
void IS31FL3736_mono_set_brightness_all(uint8_t value);
|
||||
void IS31FL3736_mono_set_led_control_register(uint8_t index, bool enabled);
|
||||
void is31fl3736_mono_set_brightness(int index, uint8_t value);
|
||||
void is31fl3736_mono_set_brightness_all(uint8_t value);
|
||||
void is31fl3736_mono_set_led_control_register(uint8_t index, bool enabled);
|
||||
|
||||
// This should not be called from an interrupt
|
||||
// (eg. from a timer interrupt).
|
||||
// Call this while idle (in between matrix scans).
|
||||
// If the buffer is dirty, it will update the driver with the buffer.
|
||||
void IS31FL3736_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void IS31FL3736_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
void is31fl3736_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void is31fl3736_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
|
||||
#define PUR_0R 0x00 // No PUR resistor
|
||||
#define PUR_05KR 0x01 // 0.5k Ohm resistor
|
||||
|
@ -80,7 +80,7 @@ uint8_t g_twi_transfer_buffer[20];
|
||||
// The control buffers match the PG0 LED On/Off registers.
|
||||
// Storing them like this is optimal for I2C transfers to the registers.
|
||||
// We could optimize this and take out the unused registers from these
|
||||
// buffers and the transfers in IS31FL3737_write_pwm_buffer() but it's
|
||||
// buffers and the transfers in is31fl3737_write_pwm_buffer() but it's
|
||||
// probably not worth the extra complexity.
|
||||
|
||||
uint8_t g_pwm_buffer[DRIVER_COUNT][192];
|
||||
@ -89,7 +89,7 @@ bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
|
||||
uint8_t g_led_control_registers[DRIVER_COUNT][24] = {0};
|
||||
bool g_led_control_registers_update_required[DRIVER_COUNT] = {false};
|
||||
|
||||
void IS31FL3737_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
void is31fl3737_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
g_twi_transfer_buffer[0] = reg;
|
||||
g_twi_transfer_buffer[1] = data;
|
||||
|
||||
@ -102,7 +102,7 @@ void IS31FL3737_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void IS31FL3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
void is31fl3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
// assumes PG1 is already selected
|
||||
|
||||
// transmit PWM registers in 12 transfers of 16 bytes
|
||||
@ -126,56 +126,59 @@ void IS31FL3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3737_init(uint8_t addr) {
|
||||
void is31fl3737_init(uint8_t addr) {
|
||||
// In order to avoid the LEDs being driven with garbage data
|
||||
// in the LED driver's PWM registers, shutdown is enabled last.
|
||||
// Set up the mode and other settings, clear the PWM registers,
|
||||
// then disable software shutdown.
|
||||
|
||||
// Unlock the command register.
|
||||
IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
|
||||
// Select PG0
|
||||
IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
|
||||
is31fl3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
|
||||
// Turn off all LEDs.
|
||||
for (int i = 0x00; i <= 0x17; i++) {
|
||||
IS31FL3737_write_register(addr, i, 0x00);
|
||||
is31fl3737_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// Unlock the command register.
|
||||
IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
|
||||
// Select PG1
|
||||
IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
|
||||
is31fl3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
|
||||
// Set PWM on all LEDs to 0
|
||||
// No need to setup Breath registers to PWM as that is the default.
|
||||
for (int i = 0x00; i <= 0xBF; i++) {
|
||||
IS31FL3737_write_register(addr, i, 0x00);
|
||||
is31fl3737_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// Unlock the command register.
|
||||
IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
|
||||
// Select PG3
|
||||
IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
|
||||
is31fl3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
|
||||
// Set de-ghost pull-up resistors (SWx)
|
||||
IS31FL3737_write_register(addr, ISSI_REG_SWPULLUP, ISSI_SWPULLUP);
|
||||
is31fl3737_write_register(addr, ISSI_REG_SWPULLUP, ISSI_SWPULLUP);
|
||||
// Set de-ghost pull-down resistors (CSx)
|
||||
IS31FL3737_write_register(addr, ISSI_REG_CSPULLUP, ISSI_CSPULLUP);
|
||||
is31fl3737_write_register(addr, ISSI_REG_CSPULLUP, ISSI_CSPULLUP);
|
||||
// Set global current to maximum.
|
||||
IS31FL3737_write_register(addr, ISSI_REG_GLOBALCURRENT, ISSI_GLOBALCURRENT);
|
||||
is31fl3737_write_register(addr, ISSI_REG_GLOBALCURRENT, ISSI_GLOBALCURRENT);
|
||||
// Disable software shutdown.
|
||||
IS31FL3737_write_register(addr, ISSI_REG_CONFIGURATION, ((ISSI_PWM_FREQUENCY & 0b111) << 3) | 0x01);
|
||||
is31fl3737_write_register(addr, ISSI_REG_CONFIGURATION, ((ISSI_PWM_FREQUENCY & 0b111) << 3) | 0x01);
|
||||
|
||||
// Wait 10ms to ensure the device has woken up.
|
||||
wait_ms(10);
|
||||
}
|
||||
|
||||
void IS31FL3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
void is31fl3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
is31_led led;
|
||||
if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
|
||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||
|
||||
if (g_pwm_buffer[led.driver][led.r] == red && g_pwm_buffer[led.driver][led.g] == green && g_pwm_buffer[led.driver][led.b] == blue) {
|
||||
return;
|
||||
}
|
||||
g_pwm_buffer[led.driver][led.r] = red;
|
||||
g_pwm_buffer[led.driver][led.g] = green;
|
||||
g_pwm_buffer[led.driver][led.b] = blue;
|
||||
@ -183,13 +186,13 @@ void IS31FL3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3737_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
void is31fl3737_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
for (int i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
|
||||
IS31FL3737_set_color(i, red, green, blue);
|
||||
is31fl3737_set_color(i, red, green, blue);
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3737_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
|
||||
void is31fl3737_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
|
||||
is31_led led;
|
||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||
|
||||
@ -219,24 +222,24 @@ void IS31FL3737_set_led_control_register(uint8_t index, bool red, bool green, bo
|
||||
g_led_control_registers_update_required[led.driver] = true;
|
||||
}
|
||||
|
||||
void IS31FL3737_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||
void is31fl3737_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||
if (g_pwm_buffer_update_required[index]) {
|
||||
// Firstly we need to unlock the command register and select PG1
|
||||
IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
|
||||
is31fl3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
|
||||
|
||||
IS31FL3737_write_pwm_buffer(addr, g_pwm_buffer[index]);
|
||||
is31fl3737_write_pwm_buffer(addr, g_pwm_buffer[index]);
|
||||
}
|
||||
g_pwm_buffer_update_required[index] = false;
|
||||
}
|
||||
|
||||
void IS31FL3737_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
void is31fl3737_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
if (g_led_control_registers_update_required[index]) {
|
||||
// Firstly we need to unlock the command register and select PG0
|
||||
IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
|
||||
is31fl3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
|
||||
for (int i = 0; i < 24; i++) {
|
||||
IS31FL3737_write_register(addr, i, g_led_control_registers[index][i]);
|
||||
is31fl3737_write_register(addr, i, g_led_control_registers[index][i]);
|
||||
}
|
||||
}
|
||||
g_led_control_registers_update_required[index] = false;
|
||||
|
@ -33,21 +33,21 @@ typedef struct is31_led {
|
||||
|
||||
extern const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT];
|
||||
|
||||
void IS31FL3737_init(uint8_t addr);
|
||||
void IS31FL3737_write_register(uint8_t addr, uint8_t reg, uint8_t data);
|
||||
void IS31FL3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
|
||||
void is31fl3737_init(uint8_t addr);
|
||||
void is31fl3737_write_register(uint8_t addr, uint8_t reg, uint8_t data);
|
||||
void is31fl3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
|
||||
|
||||
void IS31FL3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void IS31FL3737_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
|
||||
void is31fl3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void is31fl3737_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
|
||||
|
||||
void IS31FL3737_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
|
||||
void is31fl3737_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
|
||||
|
||||
// This should not be called from an interrupt
|
||||
// (eg. from a timer interrupt).
|
||||
// Call this while idle (in between matrix scans).
|
||||
// If the buffer is dirty, it will update the driver with the buffer.
|
||||
void IS31FL3737_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void IS31FL3737_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
void is31fl3737_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void is31fl3737_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
|
||||
#define PUR_0R 0x00 // No PUR resistor
|
||||
#define PUR_05KR 0x01 // 0.5k Ohm resistor in t_NOL
|
||||
|
@ -82,7 +82,7 @@ uint8_t g_twi_transfer_buffer[20] = {0xFF};
|
||||
// The scaling buffers match the PG2 and PG3 LED On/Off registers.
|
||||
// Storing them like this is optimal for I2C transfers to the registers.
|
||||
// We could optimize this and take out the unused registers from these
|
||||
// buffers and the transfers in IS31FL3741_write_pwm_buffer() but it's
|
||||
// buffers and the transfers in is31fl3741_write_pwm_buffer() but it's
|
||||
// probably not worth the extra complexity.
|
||||
uint8_t g_pwm_buffer[DRIVER_COUNT][ISSI_MAX_LEDS];
|
||||
bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
|
||||
@ -90,7 +90,7 @@ bool g_scaling_registers_update_required[DRIVER_COUNT] = {false};
|
||||
|
||||
uint8_t g_scaling_registers[DRIVER_COUNT][ISSI_MAX_LEDS];
|
||||
|
||||
void IS31FL3741_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
void is31fl3741_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
g_twi_transfer_buffer[0] = reg;
|
||||
g_twi_transfer_buffer[1] = data;
|
||||
|
||||
@ -103,14 +103,14 @@ void IS31FL3741_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IS31FL3741_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
bool is31fl3741_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
// Assume PG0 is already selected
|
||||
|
||||
for (int i = 0; i < 342; i += 18) {
|
||||
if (i == 180) {
|
||||
// unlock the command register and select PG1
|
||||
IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM1);
|
||||
is31fl3741_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3741_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM1);
|
||||
}
|
||||
|
||||
g_twi_transfer_buffer[0] = i % 180;
|
||||
@ -148,7 +148,7 @@ bool IS31FL3741_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void IS31FL3741_init(uint8_t addr) {
|
||||
void is31fl3741_init(uint8_t addr) {
|
||||
// In order to avoid the LEDs being driven with garbage data
|
||||
// in the LED driver's PWM registers, shutdown is enabled last.
|
||||
// Set up the mode and other settings, clear the PWM registers,
|
||||
@ -156,44 +156,47 @@ void IS31FL3741_init(uint8_t addr) {
|
||||
// Unlock the command register.
|
||||
|
||||
// Unlock the command register.
|
||||
IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3741_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
|
||||
// Select PG4
|
||||
IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
|
||||
is31fl3741_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
|
||||
|
||||
// Set to Normal operation
|
||||
IS31FL3741_write_register(addr, ISSI_REG_CONFIGURATION, 0x01);
|
||||
is31fl3741_write_register(addr, ISSI_REG_CONFIGURATION, 0x01);
|
||||
|
||||
// Set Golbal Current Control Register
|
||||
IS31FL3741_write_register(addr, ISSI_REG_GLOBALCURRENT, ISSI_GLOBALCURRENT);
|
||||
is31fl3741_write_register(addr, ISSI_REG_GLOBALCURRENT, ISSI_GLOBALCURRENT);
|
||||
// Set Pull up & Down for SWx CSy
|
||||
IS31FL3741_write_register(addr, ISSI_REG_PULLDOWNUP, ((ISSI_CSPULLUP << 4) | ISSI_SWPULLUP));
|
||||
is31fl3741_write_register(addr, ISSI_REG_PULLDOWNUP, ((ISSI_CSPULLUP << 4) | ISSI_SWPULLUP));
|
||||
|
||||
// IS31FL3741_update_led_scaling_registers(addr, 0xFF, 0xFF, 0xFF);
|
||||
// is31fl3741_update_led_scaling_registers(addr, 0xFF, 0xFF, 0xFF);
|
||||
|
||||
// Wait 10ms to ensure the device has woken up.
|
||||
wait_ms(10);
|
||||
}
|
||||
|
||||
void IS31FL3741_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
void is31fl3741_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
is31_led led;
|
||||
if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
|
||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||
|
||||
if (g_pwm_buffer[led.driver][led.r] == red && g_pwm_buffer[led.driver][led.g] == green && g_pwm_buffer[led.driver][led.b] == blue) {
|
||||
return;
|
||||
}
|
||||
g_pwm_buffer_update_required[led.driver] = true;
|
||||
g_pwm_buffer[led.driver][led.r] = red;
|
||||
g_pwm_buffer[led.driver][led.g] = green;
|
||||
g_pwm_buffer[led.driver][led.b] = blue;
|
||||
g_pwm_buffer_update_required[led.driver] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3741_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
void is31fl3741_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
for (int i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
|
||||
IS31FL3741_set_color(i, red, green, blue);
|
||||
is31fl3741_set_color(i, red, green, blue);
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3741_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
|
||||
void is31fl3741_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
|
||||
is31_led led;
|
||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||
|
||||
@ -218,19 +221,19 @@ void IS31FL3741_set_led_control_register(uint8_t index, bool red, bool green, bo
|
||||
g_scaling_registers_update_required[led.driver] = true;
|
||||
}
|
||||
|
||||
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]) {
|
||||
// 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_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]);
|
||||
}
|
||||
|
||||
g_pwm_buffer_update_required[index] = false;
|
||||
}
|
||||
|
||||
void IS31FL3741_set_pwm_buffer(const is31_led *pled, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
void is31fl3741_set_pwm_buffer(const is31_led *pled, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
g_pwm_buffer[pled->driver][pled->r] = red;
|
||||
g_pwm_buffer[pled->driver][pled->g] = green;
|
||||
g_pwm_buffer[pled->driver][pled->b] = blue;
|
||||
@ -238,31 +241,31 @@ void IS31FL3741_set_pwm_buffer(const is31_led *pled, uint8_t red, uint8_t green,
|
||||
g_pwm_buffer_update_required[pled->driver] = true;
|
||||
}
|
||||
|
||||
void IS31FL3741_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
void is31fl3741_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
if (g_scaling_registers_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_SCALING_0);
|
||||
is31fl3741_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3741_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_SCALING_0);
|
||||
|
||||
// CS1_SW1 to CS30_SW6 are on PG2
|
||||
for (int i = CS1_SW1; i <= CS30_SW6; ++i) {
|
||||
IS31FL3741_write_register(addr, i, g_scaling_registers[index][i]);
|
||||
is31fl3741_write_register(addr, i, g_scaling_registers[index][i]);
|
||||
}
|
||||
|
||||
// unlock the command register and select PG3
|
||||
IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
IS31FL3741_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_SCALING_1);
|
||||
is31fl3741_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
|
||||
is31fl3741_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_SCALING_1);
|
||||
|
||||
// CS1_SW7 to CS39_SW9 are on PG3
|
||||
for (int i = CS1_SW7; i <= CS39_SW9; ++i) {
|
||||
IS31FL3741_write_register(addr, i - CS1_SW7, g_scaling_registers[index][i]);
|
||||
is31fl3741_write_register(addr, i - CS1_SW7, g_scaling_registers[index][i]);
|
||||
}
|
||||
|
||||
g_scaling_registers_update_required[index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL3741_set_scaling_registers(const is31_led *pled, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
void is31fl3741_set_scaling_registers(const is31_led *pled, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
g_scaling_registers[pled->driver][pled->r] = red;
|
||||
g_scaling_registers[pled->driver][pled->g] = green;
|
||||
g_scaling_registers[pled->driver][pled->b] = blue;
|
||||
|
@ -32,24 +32,24 @@ typedef struct is31_led {
|
||||
|
||||
extern const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT];
|
||||
|
||||
void IS31FL3741_init(uint8_t addr);
|
||||
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);
|
||||
void is31fl3741_init(uint8_t addr);
|
||||
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);
|
||||
|
||||
void IS31FL3741_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void IS31FL3741_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
|
||||
void is31fl3741_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void is31fl3741_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
|
||||
|
||||
void IS31FL3741_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
|
||||
void is31fl3741_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
|
||||
|
||||
// This should not be called from an interrupt
|
||||
// (eg. from a timer interrupt).
|
||||
// Call this while idle (in between matrix scans).
|
||||
// If the buffer is dirty, it will update the driver with the buffer.
|
||||
void IS31FL3741_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void IS31FL3741_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
void IS31FL3741_set_scaling_registers(const is31_led *pled, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void is31fl3741_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void is31fl3741_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
void is31fl3741_set_scaling_registers(const is31_led *pled, uint8_t red, uint8_t green, uint8_t blue);
|
||||
|
||||
void IS31FL3741_set_pwm_buffer(const is31_led *pled, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void is31fl3741_set_pwm_buffer(const is31_led *pled, uint8_t red, uint8_t green, uint8_t blue);
|
||||
|
||||
#define PUR_0R 0x00 // No PUR resistor
|
||||
#define PUR_05KR 0x01 // 0.5k Ohm resistor
|
||||
|
@ -133,19 +133,28 @@ void IS31FL_common_update_pwm_register(uint8_t addr, uint8_t index) {
|
||||
|
||||
#ifdef ISSI_MANUAL_SCALING
|
||||
void IS31FL_set_manual_scaling_buffer(void) {
|
||||
is31_led led;
|
||||
is31_led scale;
|
||||
for (int i = 0; i < ISSI_MANUAL_SCALING; i++) {
|
||||
is31_led scale = g_is31_scaling[i];
|
||||
memcpy_P(&scale, (&g_is31_scaling[i]), sizeof(scale));
|
||||
|
||||
# ifdef RGB_MATRIX_ENABLE
|
||||
if (scale.driver >= 0 && scale.driver < RGB_MATRIX_LED_COUNT) {
|
||||
is31_led led = g_is31_leds[scale.driver];
|
||||
memcpy_P(&led, (&g_is31_leds[scale.driver]), sizeof(led));
|
||||
|
||||
if (g_scaling_buffer[led.driver][led.r] = scale.r && g_scaling_buffer[led.driver][led.g] = scale.g && g_scaling_buffer[led.driver][led.b] = scale.b) {
|
||||
return;
|
||||
}
|
||||
g_scaling_buffer[led.driver][led.r] = scale.r;
|
||||
g_scaling_buffer[led.driver][led.g] = scale.g;
|
||||
g_scaling_buffer[led.driver][led.b] = scale.b;
|
||||
# elif defined(LED_MATRIX_ENABLE)
|
||||
if (scale.driver >= 0 && scale.driver < LED_MATRIX_LED_COUNT) {
|
||||
is31_led led = g_is31_leds[scale.driver];
|
||||
memcpy_P(&led, (&g_is31_leds[scale.driver]), sizeof(led));
|
||||
|
||||
if (g_scaling_buffer[led.driver][led.v] == scale.v) {
|
||||
return;
|
||||
}
|
||||
g_scaling_buffer[led.driver][led.v] = scale.v;
|
||||
# endif
|
||||
g_scaling_buffer_update_required[led.driver] = true;
|
||||
@ -169,7 +178,8 @@ void IS31FL_common_update_scaling_register(uint8_t addr, uint8_t index) {
|
||||
// Colour is set by adjusting PWM register
|
||||
void IS31FL_RGB_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
|
||||
is31_led led = g_is31_leds[index];
|
||||
is31_led led;
|
||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||
|
||||
g_pwm_buffer[led.driver][led.r] = red;
|
||||
g_pwm_buffer[led.driver][led.g] = green;
|
||||
@ -186,7 +196,8 @@ void IS31FL_RGB_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
|
||||
// Setup Scaling register that decides the peak current of each LED
|
||||
void IS31FL_RGB_set_scaling_buffer(uint8_t index, bool red, bool green, bool blue) {
|
||||
is31_led led = g_is31_leds[index];
|
||||
is31_led led;
|
||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||
if (red) {
|
||||
g_scaling_buffer[led.driver][led.r] = ISSI_SCAL_RED;
|
||||
} else {
|
||||
@ -208,7 +219,8 @@ void IS31FL_RGB_set_scaling_buffer(uint8_t index, bool red, bool green, bool blu
|
||||
#elif defined(LED_MATRIX_ENABLE)
|
||||
// LED Matrix Specific scripts
|
||||
void IS31FL_simple_set_scaling_buffer(uint8_t index, bool value) {
|
||||
is31_led led = g_is31_leds[index];
|
||||
is31_led led;
|
||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||
if (value) {
|
||||
g_scaling_buffer[led.driver][led.v] = ISSI_SCAL_LED;
|
||||
} else {
|
||||
@ -219,7 +231,9 @@ void IS31FL_simple_set_scaling_buffer(uint8_t index, bool value) {
|
||||
|
||||
void IS31FL_simple_set_brightness(int index, uint8_t value) {
|
||||
if (index >= 0 && index < LED_MATRIX_LED_COUNT) {
|
||||
is31_led led = g_is31_leds[index];
|
||||
is31_led led;
|
||||
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
|
||||
|
||||
g_pwm_buffer[led.driver][led.v] = value;
|
||||
g_pwm_buffer_update_required[led.driver] = true;
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ typedef struct is31_led {
|
||||
uint8_t b;
|
||||
} __attribute__((packed)) is31_led;
|
||||
|
||||
extern const is31_led __flash g_is31_leds[RGB_MATRIX_LED_COUNT];
|
||||
extern const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT];
|
||||
|
||||
#elif defined(LED_MATRIX_ENABLE)
|
||||
typedef struct is31_led {
|
||||
@ -51,11 +51,11 @@ typedef struct is31_led {
|
||||
uint8_t v;
|
||||
} __attribute__((packed)) is31_led;
|
||||
|
||||
extern const is31_led __flash g_is31_leds[LED_MATRIX_LED_COUNT];
|
||||
extern const is31_led PROGMEM g_is31_leds[LED_MATRIX_LED_COUNT];
|
||||
#endif
|
||||
|
||||
#ifdef ISSI_MANUAL_SCALING
|
||||
extern const is31_led __flash g_is31_scaling[];
|
||||
extern const is31_led PROGMEM g_is31_scaling[];
|
||||
void IS31FL_set_manual_scaling_buffer(void);
|
||||
#endif
|
||||
|
||||
|
@ -19,8 +19,8 @@
|
||||
#define ILI9XXX_CMD_SLEEP_OFF 0x11 // Exist sleep mode
|
||||
#define ILI9XXX_CMD_PARTIAL_ON 0x12 // Enter partial mode
|
||||
#define ILI9XXX_CMD_PARTIAL_OFF 0x13 // Exit partial mode
|
||||
#define ILI9XXX_CMD_INVERT_ON 0x20 // Enter inverted mode
|
||||
#define ILI9XXX_CMD_INVERT_OFF 0x21 // Exit inverted mode
|
||||
#define ILI9XXX_CMD_INVERT_OFF 0x20 // Exit inverted mode
|
||||
#define ILI9XXX_CMD_INVERT_ON 0x21 // Enter inverted mode
|
||||
#define ILI9XXX_SET_GAMMA 0x26 // Set gamma params
|
||||
#define ILI9XXX_CMD_DISPLAY_OFF 0x28 // Disable display
|
||||
#define ILI9XXX_CMD_DISPLAY_ON 0x29 // Enable display
|
||||
|
@ -191,13 +191,12 @@ static inline void ps2_mouse_convert_report_to_hid(report_mouse_t *mouse_report)
|
||||
|
||||
#ifdef PS2_MOUSE_INVERT_BUTTONS
|
||||
// swap left & right buttons
|
||||
uint8_t needs_left = mouse_report->buttons & PS2_MOUSE_BTN_RIGHT;
|
||||
uint8_t needs_right = mouse_report->buttons & PS2_MOUSE_BTN_LEFT;
|
||||
mouse_report->buttons = (mouse_report->buttons & ~(PS2_MOUSE_BTN_MASK)) | (needs_left ? PS2_MOUSE_BTN_LEFT : 0) | (needs_right ? PS2_MOUSE_BTN_RIGHT : 0);
|
||||
#else
|
||||
bool needs_left = mouse_report->buttons & (1 << PS2_MOUSE_BTN_RIGHT);
|
||||
bool needs_right = mouse_report->buttons & (1 << PS2_MOUSE_BTN_LEFT);
|
||||
mouse_report->buttons = (mouse_report->buttons & ~((1 << PS2_MOUSE_BTN_LEFT) | (1 << PS2_MOUSE_BTN_RIGHT))) | (needs_left << PS2_MOUSE_BTN_LEFT) | (needs_right << PS2_MOUSE_BTN_RIGHT);
|
||||
#endif
|
||||
// remove sign and overflow flags
|
||||
mouse_report->buttons &= PS2_MOUSE_BTN_MASK;
|
||||
#endif
|
||||
|
||||
#ifdef PS2_MOUSE_INVERT_X
|
||||
mouse_report->x = -mouse_report->x;
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "quantum.h" //to get is_keyboard_left
|
||||
#include "keyboard.h"
|
||||
#include <stdint.h>
|
||||
#include "spi_master.h"
|
||||
#include "util.h"
|
||||
|
@ -16,21 +16,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
/* underglow */
|
||||
#define RGBLED_NUM 24
|
||||
#define RGBLIGHT_EFFECT_BREATHING
|
||||
#define RGBLIGHT_EFFECT_RAINBOW_MOOD
|
||||
#define RGBLIGHT_EFFECT_RAINBOW_SWIRL
|
||||
#define RGBLIGHT_EFFECT_SNAKE
|
||||
#define RGBLIGHT_EFFECT_KNIGHT
|
||||
#define RGBLIGHT_EFFECT_CHRISTMAS
|
||||
#define RGBLIGHT_EFFECT_STATIC_GRADIENT
|
||||
#define RGBLIGHT_EFFECT_RGB_TEST
|
||||
#define RGBLIGHT_EFFECT_ALTERNATING
|
||||
#define RGBLIGHT_EFFECT_TWINKLE
|
||||
#define RGBLIGHT
|
||||
|
||||
/*
|
||||
* Feature disable options
|
||||
* These options are also useful to firmware size reduction.
|
||||
|
@ -10,6 +10,19 @@
|
||||
"pin": "B6"
|
||||
},
|
||||
"rgblight": {
|
||||
"max_brightness": 185
|
||||
"led_count": 24,
|
||||
"max_brightness": 185,
|
||||
"animations": {
|
||||
"breathing": true,
|
||||
"rainbow_mood": true,
|
||||
"rainbow_swirl": true,
|
||||
"snake": true,
|
||||
"knight": true,
|
||||
"christmas": true,
|
||||
"static_gradient": true,
|
||||
"rgb_test": true,
|
||||
"alternating": true,
|
||||
"twinkle": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,23 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// clang-format off
|
||||
|
||||
#define TAP_CODE_DELAY 10
|
||||
|
||||
#define RGBLED_NUM 4
|
||||
#define RGBLIGHT_HUE_STEP 8
|
||||
#define RGBLIGHT_SAT_STEP 8
|
||||
#define RGBLIGHT_VAL_STEP 8
|
||||
#define RGBLIGHT_SLEEP /* If defined, the RGB lighting will be switched off when the host goes to sleep */
|
||||
#define RGBLIGHT_EFFECT_BREATHING
|
||||
#define RGBLIGHT_EFFECT_RAINBOW_MOOD
|
||||
#define RGBLIGHT_EFFECT_RAINBOW_SWIRL
|
||||
#define RGBLIGHT_EFFECT_SNAKE
|
||||
#define RGBLIGHT_EFFECT_KNIGHT
|
||||
#define RGBLIGHT_EFFECT_CHRISTMAS
|
||||
#define RGBLIGHT_EFFECT_STATIC_GRADIENT
|
||||
#define RGBLIGHT_EFFECT_RGB_TEST
|
||||
#define RGBLIGHT_EFFECT_ALTERNATING
|
||||
#define RGBLIGHT_EFFECT_TWINKLE
|
||||
/* default setup after eeprom reset */
|
||||
#define RGBLIGHT_DEFAULT_MODE RGBLIGHT_EFFECT_BREATHING + 2
|
||||
#define RGBLIGHT_DEFAULT_HUE 152
|
||||
|
@ -18,9 +18,30 @@
|
||||
{"pin_a": "F6", "pin_b": "F5"}
|
||||
]
|
||||
},
|
||||
"qmk": {
|
||||
"tap_keycode_delay": 10
|
||||
},
|
||||
"qmk_lufa_bootloader": {
|
||||
"led": "B0"
|
||||
},
|
||||
"rgblight": {
|
||||
"saturation_steps": 8,
|
||||
"brightness_steps": 8,
|
||||
"led_count": 4,
|
||||
"sleep": true,
|
||||
"animations": {
|
||||
"breathing": true,
|
||||
"rainbow_mood": true,
|
||||
"rainbow_swirl": true,
|
||||
"snake": true,
|
||||
"knight": true,
|
||||
"christmas": true,
|
||||
"static_gradient": true,
|
||||
"rgb_test": true,
|
||||
"alternating": true,
|
||||
"twinkle": true
|
||||
}
|
||||
},
|
||||
"ws2812": {
|
||||
"pin": "D3"
|
||||
},
|
||||
|
@ -14,4 +14,3 @@ AUDIO_ENABLE = no # Audio output
|
||||
ENCODER_ENABLE = yes
|
||||
LTO_ENABLE = yes
|
||||
OLED_ENABLE = yes
|
||||
OLED_DRIVER = SSD1306
|
||||
|
@ -36,7 +36,7 @@
|
||||
]
|
||||
},
|
||||
"rgb_matrix": {
|
||||
"driver": "WS2812",
|
||||
"driver": "ws2812",
|
||||
"layout": [
|
||||
{"flags": 4, "matrix": [0, 2], "x": 0, "y": 0},
|
||||
{"flags": 4, "matrix": [1, 0], "x": 20, "y": 0},
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user