Merge branch 'develop' into feature/h7-spi

This commit is contained in:
Pablo Martínez 2023-10-07 13:40:11 +02:00 committed by GitHub
commit c0a89694eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2539 changed files with 76776 additions and 27951 deletions

4
.github/labeler.yml vendored
View File

@ -40,3 +40,7 @@ translation:
- docs/ru-ru/**/*
CI:
- .github/**/*
dd:
- data/constants/**/*
- data/mappings/**/*
- data/schemas/**/*

View File

@ -25,7 +25,7 @@ jobs:
if: github.repository == 'qmk/qmk_firmware'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 1
persist-credentials: false

View File

@ -27,7 +27,7 @@ jobs:
if: github.repository == 'qmk/qmk_firmware'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0

View File

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

View File

@ -24,7 +24,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

View File

@ -15,7 +15,7 @@ jobs:
if: github.repository == 'qmk/qmk_firmware'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
token: ${{ secrets.QMK_BOT_TOKEN }}
fetch-depth: 0

View File

@ -23,7 +23,7 @@ jobs:
if: github.repository == 'qmk/qmk_firmware'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 1

View File

@ -21,7 +21,7 @@ jobs:
- riot
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
token: ${{ secrets.QMK_BOT_TOKEN }}
fetch-depth: 0

View File

@ -25,7 +25,7 @@ jobs:
- name: Disable safe.directory check
run : git config --global --add safe.directory '*'
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
@ -35,7 +35,7 @@ jobs:
- name: Get changed files
id: file_changes
uses: tj-actions/changed-files@v38
uses: tj-actions/changed-files@v39
- name: Run qmk formatters
shell: 'bash {0}'

View File

@ -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
with:
fetch-depth: 0

View File

@ -18,7 +18,7 @@ jobs:
- name: Disable safe.directory check
run : git config --global --add safe.directory '*'
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
@ -27,7 +27,7 @@ jobs:
- name: Get changed files
id: file_changes
uses: tj-actions/changed-files@v38
uses: tj-actions/changed-files@v39
- name: Print info
run: |
@ -74,31 +74,10 @@ jobs:
fi
exit $exit_code
- name: Verify at most one added keyboard
- name: Verify keyboard aliases
if: always()
shell: 'bash {0}'
run: |
git reset --hard
git clean -xfd
# Get the keyboard list and count for the target branch
git checkout -f ${{ github.base_ref }}
git pull --ff-only
QMK_KEYBOARDS_BASE=$(qmk list-keyboards)
QMK_KEYBOARDS_BASE_COUNT=$(qmk list-keyboards | wc -l)
# Get the keyboard list and count for the PR
git checkout -f ${{ github.head_ref }}
git merge --no-commit --squash ${{ github.base_ref }}
QMK_KEYBOARDS_PR=$(qmk list-keyboards)
QMK_KEYBOARDS_PR_COUNT=$(qmk list-keyboards | wc -l)
echo "::group::Keyboards changes in this PR"
diff -d -U 0 <(echo "$QMK_KEYBOARDS_BASE") <(echo "$QMK_KEYBOARDS_PR") | grep -vE '^(---|\+\+\+|@@)' | sed -e 's@^-@Removed: @g' -e 's@^+@ Added: @g'
echo "::endgroup::"
if [[ $QMK_KEYBOARDS_PR_COUNT -gt $(($QMK_KEYBOARDS_BASE_COUNT + 1)) ]]; then
echo "More than one keyboard added in this PR -- see the PR Checklist."
echo "::error::More than one keyboard added in this PR -- see the PR Checklist."
exit 1
fi
qmk ci-validate-aliases

View File

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

View File

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

View File

@ -26,7 +26,7 @@ jobs:
container: ghcr.io/qmk/qmk_cli
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install dependencies

1
.gitignore vendored
View File

@ -95,6 +95,7 @@ secrets.tar
# Python things
__pycache__
.python-version
.venv
# Prerequisites for updating ChibiOS
/util/fmpp*

View File

@ -300,17 +300,18 @@ endef
define BUILD_TEST
TEST_PATH := $1
TEST_NAME := $$(notdir $$(TEST_PATH))
TEST_FULL_NAME := $$(subst /,_,$$(patsubst $$(ROOT_DIR)tests/%,%,$$(TEST_PATH)))
MAKE_TARGET := $2
COMMAND := $1
MAKE_CMD := $$(MAKE) -r -R -C $(ROOT_DIR) -f $(BUILDDEFS_PATH)/build_test.mk $$(MAKE_TARGET)
MAKE_VARS := TEST=$$(TEST_NAME) TEST_PATH=$$(TEST_PATH) FULL_TESTS="$$(FULL_TESTS)"
MAKE_VARS := TEST=$$(TEST_NAME) TEST_OUTPUT=$$(TEST_FULL_NAME) TEST_PATH=$$(TEST_PATH) FULL_TESTS="$$(FULL_TESTS)"
MAKE_MSG := $$(MSG_MAKE_TEST)
$$(eval $$(call BUILD))
ifneq ($$(MAKE_TARGET),clean)
TEST_EXECUTABLE := $$(TEST_OUTPUT_DIR)/$$(TEST_NAME).elf
TESTS += $$(TEST_NAME)
TEST_EXECUTABLE := $$(TEST_OUTPUT_DIR)/$$(TEST_FULL_NAME).elf
TESTS += $$(TEST_FULL_NAME)
TEST_MSG := $$(MSG_TEST)
$$(TEST_NAME)_COMMAND := \
$$(TEST_FULL_NAME)_COMMAND := \
printf "$$(TEST_MSG)\n"; \
$$(TEST_EXECUTABLE); \
if [ $$$$? -gt 0 ]; \
@ -322,15 +323,22 @@ endef
define PARSE_TEST
TESTS :=
TEST_NAME := $$(firstword $$(subst :, ,$$(RULE)))
TEST_TARGET := $$(subst $$(TEST_NAME),,$$(subst $$(TEST_NAME):,,$$(RULE)))
# list of possible targets, colon-delimited, to reassign to MAKE_TARGET and remove
TARGETS := :clean:
ifneq (,$$(findstring :$$(lastword $$(subst :, ,$$(RULE))):, $$(TARGETS)))
MAKE_TARGET := $$(lastword $$(subst :, ,$$(RULE)))
TEST_SUBPATH := $$(subst $$(eval) ,/,$$(wordlist 2, $$(words $$(subst :, ,$$(RULE))), _ $$(subst :, ,$$(RULE))))
else
MAKE_TARGET :=
TEST_SUBPATH := $$(subst :,/,$$(RULE))
endif
include $(BUILDDEFS_PATH)/testlist.mk
ifeq ($$(TEST_NAME),all)
ifeq ($$(RULE),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 /$$(TEST_SUBPATH)/, $$(patsubst %,%/,$$(TEST))), $$(TEST),))
endif
$$(foreach TEST,$$(MATCHED_TESTS),$$(eval $$(call BUILD_TEST,$$(TEST),$$(TEST_TARGET))))
$$(foreach TEST,$$(MATCHED_TESTS),$$(eval $$(call BUILD_TEST,$$(TEST),$$(MAKE_TARGET))))
endef

View File

@ -13,10 +13,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
$(TEST)_INC := \
$(TEST_OUTPUT)_INC := \
tests/test_common/common_config.h
$(TEST)_SRC := \
$(TEST_OUTPUT)_SRC := \
$(QUANTUM_SRC) \
$(SRC) \
$(QUANTUM_PATH)/keymap_introspection.c \
@ -30,8 +30,8 @@ $(TEST)_SRC := \
tests/test_common/test_logger.cpp \
$(patsubst $(ROOTDIR)/%,%,$(wildcard $(TEST_PATH)/*.cpp))
$(TEST)_DEFS := $(OPT_DEFS) "-DKEYMAP_C=\"keymap.c\""
$(TEST_OUTPUT)_DEFS := $(OPT_DEFS) "-DKEYMAP_C=\"keymap.c\""
$(TEST)_CONFIG := $(TEST_PATH)/config.h
$(TEST_OUTPUT)_CONFIG := $(TEST_PATH)/config.h
VPATH += $(TOP_DIR)/tests/test_common

View File

@ -1,17 +1,17 @@
# Look for a json keymap file
ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_5)/keymap.json)","")
KEYMAP_JSON := $(MAIN_KEYMAP_PATH_5)/keymap.json
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_5)
KEYMAP_JSON_PATH := $(MAIN_KEYMAP_PATH_5)
else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_4)/keymap.json)","")
KEYMAP_JSON := $(MAIN_KEYMAP_PATH_4)/keymap.json
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_4)
KEYMAP_JSON_PATH := $(MAIN_KEYMAP_PATH_4)
else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_3)/keymap.json)","")
KEYMAP_JSON := $(MAIN_KEYMAP_PATH_3)/keymap.json
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_3)
KEYMAP_JSON_PATH := $(MAIN_KEYMAP_PATH_3)
else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_2)/keymap.json)","")
KEYMAP_JSON := $(MAIN_KEYMAP_PATH_2)/keymap.json
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_2)
KEYMAP_JSON_PATH := $(MAIN_KEYMAP_PATH_2)
else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_1)/keymap.json)","")
KEYMAP_JSON := $(MAIN_KEYMAP_PATH_1)/keymap.json
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_1)
KEYMAP_JSON_PATH := $(MAIN_KEYMAP_PATH_1)
endif

View File

@ -127,39 +127,44 @@ include $(INFO_RULES_MK)
include $(BUILDDEFS_PATH)/build_json.mk
# Pull in keymap level rules.mk
ifeq ("$(wildcard $(KEYMAP_PATH))", "")
# Look through the possible keymap folders until we find a matching keymap.c
ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_1)/keymap.c)","")
-include $(MAIN_KEYMAP_PATH_1)/rules.mk
KEYMAP_C := $(MAIN_KEYMAP_PATH_1)/keymap.c
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_1)
else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_2)/keymap.c)","")
-include $(MAIN_KEYMAP_PATH_2)/rules.mk
KEYMAP_C := $(MAIN_KEYMAP_PATH_2)/keymap.c
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_2)
else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_3)/keymap.c)","")
-include $(MAIN_KEYMAP_PATH_3)/rules.mk
KEYMAP_C := $(MAIN_KEYMAP_PATH_3)/keymap.c
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_3)
else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_4)/keymap.c)","")
-include $(MAIN_KEYMAP_PATH_4)/rules.mk
KEYMAP_C := $(MAIN_KEYMAP_PATH_4)/keymap.c
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_4)
else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_5)/keymap.c)","")
-include $(MAIN_KEYMAP_PATH_5)/rules.mk
KEYMAP_C := $(MAIN_KEYMAP_PATH_5)/keymap.c
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_5)
else ifneq ($(LAYOUTS),)
# If we haven't found a keymap yet fall back to community layouts
include $(BUILDDEFS_PATH)/build_layout.mk
else
$(call CATASTROPHIC_ERROR,Invalid keymap,Could not find keymap)
# this state should never be reached
endif
# Look through the possible keymap folders until we find a matching keymap.c
ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_1)/keymap.c)","")
-include $(MAIN_KEYMAP_PATH_1)/rules.mk
KEYMAP_C := $(MAIN_KEYMAP_PATH_1)/keymap.c
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_1)
else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_2)/keymap.c)","")
-include $(MAIN_KEYMAP_PATH_2)/rules.mk
KEYMAP_C := $(MAIN_KEYMAP_PATH_2)/keymap.c
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_2)
else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_3)/keymap.c)","")
-include $(MAIN_KEYMAP_PATH_3)/rules.mk
KEYMAP_C := $(MAIN_KEYMAP_PATH_3)/keymap.c
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_3)
else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_4)/keymap.c)","")
-include $(MAIN_KEYMAP_PATH_4)/rules.mk
KEYMAP_C := $(MAIN_KEYMAP_PATH_4)/keymap.c
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_4)
else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_5)/keymap.c)","")
-include $(MAIN_KEYMAP_PATH_5)/rules.mk
KEYMAP_C := $(MAIN_KEYMAP_PATH_5)/keymap.c
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_5)
else ifneq ($(LAYOUTS),)
# If we haven't found a keymap yet fall back to community layouts
include $(BUILDDEFS_PATH)/build_layout.mk
# Not finding keymap.c is fine if we found a keymap.json
else ifeq ("$(wildcard $(KEYMAP_JSON_PATH))", "")
$(call CATASTROPHIC_ERROR,Invalid keymap,Could not find keymap)
# this state should never be reached
endif
# Have we found a keymap.json?
ifneq ("$(wildcard $(KEYMAP_JSON))", "")
ifneq ("$(wildcard $(KEYMAP_C))", "")
$(call WARNING_MESSAGE,Keymap is specified as both keymap.json and keymap.c -- keymap.json file wins.)
endif
KEYMAP_PATH := $(KEYMAP_JSON_PATH)
KEYMAP_C := $(INTERMEDIATE_OUTPUT)/src/keymap.c
KEYMAP_H := $(INTERMEDIATE_OUTPUT)/src/config.h

View File

@ -9,13 +9,13 @@ OPT = g
include paths.mk
include $(BUILDDEFS_PATH)/message.mk
TARGET=test/$(TEST)
TARGET=test/$(TEST_OUTPUT)
GTEST_OUTPUT = $(BUILD_DIR)/gtest
TEST_OBJ = $(BUILD_DIR)/test_obj
OUTPUTS := $(TEST_OBJ)/$(TEST) $(GTEST_OUTPUT)
OUTPUTS := $(TEST_OBJ)/$(TEST_OUTPUT) $(GTEST_OUTPUT)
GTEST_INC := \
$(LIB_PATH)/googletest/googletest/include \
@ -71,18 +71,18 @@ ifneq ($(filter $(FULL_TESTS),$(TEST)),)
include $(BUILDDEFS_PATH)/build_full_test.mk
endif
$(TEST)_SRC += \
$(TEST_OUTPUT)_SRC += \
tests/test_common/main.cpp \
$(QUANTUM_PATH)/logging/print.c
ifneq ($(strip $(INTROSPECTION_KEYMAP_C)),)
$(TEST)_DEFS += -DINTROSPECTION_KEYMAP_C=\"$(strip $(INTROSPECTION_KEYMAP_C))\"
$(TEST_OUTPUT)_DEFS += -DINTROSPECTION_KEYMAP_C=\"$(strip $(INTROSPECTION_KEYMAP_C))\"
endif
$(TEST_OBJ)/$(TEST)_SRC := $($(TEST)_SRC)
$(TEST_OBJ)/$(TEST)_INC := $($(TEST)_INC) $(VPATH) $(GTEST_INC)
$(TEST_OBJ)/$(TEST)_DEFS := $($(TEST)_DEFS)
$(TEST_OBJ)/$(TEST)_CONFIG := $($(TEST)_CONFIG)
$(TEST_OBJ)/$(TEST_OUTPUT)_SRC := $($(TEST_OUTPUT)_SRC)
$(TEST_OBJ)/$(TEST_OUTPUT)_INC := $($(TEST_OUTPUT)_INC) $(VPATH) $(GTEST_INC)
$(TEST_OBJ)/$(TEST_OUTPUT)_DEFS := $($(TEST_OUTPUT)_DEFS)
$(TEST_OBJ)/$(TEST_OUTPUT)_CONFIG := $($(TEST_OUTPUT)_CONFIG)
include $(PLATFORM_PATH)/$(PLATFORM_KEY)/platform.mk
include $(BUILDDEFS_PATH)/common_rules.mk

View File

@ -328,6 +328,7 @@ ifeq ($(strip $(RGBLIGHT_ENABLE)), yes)
COMMON_VPATH += $(QUANTUM_DIR)/rgblight
POST_CONFIG_H += $(QUANTUM_DIR)/rgblight/rgblight_post_config.h
OPT_DEFS += -DRGBLIGHT_ENABLE
OPT_DEFS += -DRGBLIGHT_$(strip $(shell echo $(RGBLIGHT_DRIVER) | tr '[:lower:]' '[:upper:]'))
SRC += $(QUANTUM_DIR)/color.c
SRC += $(QUANTUM_DIR)/rgblight/rgblight.c
CIE1931_CURVE := yes
@ -342,20 +343,28 @@ ifeq ($(strip $(RGBLIGHT_ENABLE)), yes)
APA102_DRIVER_REQUIRED := yes
endif
ifeq ($(strip $(RGBLIGHT_DRIVER)), custom)
OPT_DEFS += -DRGBLIGHT_CUSTOM_DRIVER
ifeq ($(strip $(VELOCIKEY_ENABLE)), yes)
OPT_DEFS += -DVELOCIKEY_ENABLE
endif
endif
# Deprecated driver names - do not use
ifeq ($(strip $(LED_MATRIX_DRIVER)), aw20216)
LED_MATRIX_DRIVER := aw20216s
endif
ifeq ($(strip $(LED_MATRIX_DRIVER)), ckled2001)
LED_MATRIX_DRIVER := snled27351
endif
LED_MATRIX_ENABLE ?= no
VALID_LED_MATRIX_TYPES := is31fl3731 is31fl3742a is31fl3743a is31fl3745 is31fl3746a ckled2001 custom
# TODO: is31fl3733 is31fl3737 is31fl3741
VALID_LED_MATRIX_TYPES := is31fl3218 is31fl3731 is31fl3733 is31fl3736 is31fl3737 is31fl3741 is31fl3742a is31fl3743a is31fl3745 is31fl3746a snled27351 custom
ifeq ($(strip $(LED_MATRIX_ENABLE)), yes)
ifeq ($(filter $(LED_MATRIX_DRIVER),$(VALID_LED_MATRIX_TYPES)),)
$(call CATASTROPHIC_ERROR,Invalid LED_MATRIX_DRIVER,LED_MATRIX_DRIVER="$(LED_MATRIX_DRIVER)" is not a valid matrix type)
endif
OPT_DEFS += -DLED_MATRIX_ENABLE
OPT_DEFS += -DLED_MATRIX_$(strip $(shell echo $(LED_MATRIX_DRIVER) | tr '[:lower:]' '[:upper:]'))
ifneq (,$(filter $(MCU), atmega16u2 atmega32u2 at90usb162))
# ATmegaxxU2 does not have hardware MUL instruction - lib8tion must be told to use software multiplication routines
OPT_DEFS += -DLIB8_ATTINY
@ -370,58 +379,102 @@ endif
SRC += $(LIB_PATH)/lib8tion/lib8tion.c
CIE1931_CURVE := yes
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3218)
OPT_DEFS += -DHAL_USE_I2C=TRUE
COMMON_VPATH += $(DRIVER_PATH)/led/issi
SRC += is31fl3218-simple.c
QUANTUM_LIB_SRC += i2c_master.c
endif
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3731)
OPT_DEFS += -DIS31FL3731 -DSTM32_I2C -DHAL_USE_I2C=TRUE
OPT_DEFS += -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)
OPT_DEFS += -DIS31FLCOMMON -DIS31FL3742A -DSTM32_I2C -DHAL_USE_I2C=TRUE
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3733)
OPT_DEFS += -DHAL_USE_I2C=TRUE
COMMON_VPATH += $(DRIVER_PATH)/led/issi
SRC += is31fl3733-simple.c
QUANTUM_LIB_SRC += i2c_master.c
endif
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3736)
OPT_DEFS += -DHAL_USE_I2C=TRUE
COMMON_VPATH += $(DRIVER_PATH)/led/issi
SRC += is31fl3736-simple.c
QUANTUM_LIB_SRC += i2c_master.c
endif
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3737)
OPT_DEFS += -DHAL_USE_I2C=TRUE
COMMON_VPATH += $(DRIVER_PATH)/led/issi
SRC += is31fl3737-simple.c
QUANTUM_LIB_SRC += i2c_master.c
endif
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3741)
OPT_DEFS += -DHAL_USE_I2C=TRUE
COMMON_VPATH += $(DRIVER_PATH)/led/issi
SRC += is31fl3741-simple.c
QUANTUM_LIB_SRC += i2c_master.c
endif
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3742a)
OPT_DEFS += -DIS31FLCOMMON -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)
OPT_DEFS += -DIS31FLCOMMON -DIS31FL3743A -DSTM32_I2C -DHAL_USE_I2C=TRUE
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3743a)
OPT_DEFS += -DIS31FLCOMMON -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)
OPT_DEFS += -DIS31FLCOMMON -DIS31FL3745 -DSTM32_I2C -DHAL_USE_I2C=TRUE
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3745)
OPT_DEFS += -DIS31FLCOMMON -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)
OPT_DEFS += -DIS31FLCOMMON -DIS31FL3746A -DSTM32_I2C -DHAL_USE_I2C=TRUE
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3746a)
OPT_DEFS += -DIS31FLCOMMON -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)
OPT_DEFS += -DCKLED2001 -DSTM32_I2C -DHAL_USE_I2C=TRUE
ifeq ($(strip $(LED_MATRIX_DRIVER)), snled27351)
OPT_DEFS += -DHAL_USE_I2C=TRUE
COMMON_VPATH += $(DRIVER_PATH)/led
SRC += ckled2001-simple.c
SRC += snled27351-simple.c
QUANTUM_LIB_SRC += i2c_master.c
endif
endif
# Deprecated driver names - do not use
ifeq ($(strip $(RGB_MATRIX_DRIVER)), aw20216)
RGB_MATRIX_DRIVER := aw20216s
endif
ifeq ($(strip $(RGB_MATRIX_DRIVER)), ckled2001)
RGB_MATRIX_DRIVER := snled27351
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 := aw20216s is31fl3218 is31fl3731 is31fl3733 is31fl3736 is31fl3737 is31fl3741 is31fl3742a is31fl3743a is31fl3745 is31fl3746a snled27351 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)
endif
OPT_DEFS += -DRGB_MATRIX_ENABLE
OPT_DEFS += -DRGB_MATRIX_$(strip $(shell echo $(RGB_MATRIX_DRIVER) | tr '[:lower:]' '[:upper:]'))
ifneq (,$(filter $(MCU), atmega16u2 atmega32u2 at90usb162))
# ATmegaxxU2 does not have hardware MUL instruction - lib8tion must be told to use software multiplication routines
OPT_DEFS += -DLIB8_ATTINY
@ -437,90 +490,95 @@ endif
CIE1931_CURVE := yes
RGB_KEYCODES_ENABLE := yes
ifeq ($(strip $(RGB_MATRIX_DRIVER)), aw20216)
OPT_DEFS += -DAW20216 -DSTM32_SPI -DHAL_USE_SPI=TRUE
ifeq ($(strip $(RGB_MATRIX_DRIVER)), aw20216s)
OPT_DEFS += -DHAL_USE_SPI=TRUE
COMMON_VPATH += $(DRIVER_PATH)/led
SRC += aw20216.c
SRC += aw20216s.c
QUANTUM_LIB_SRC += spi_master.c
endif
ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3218)
OPT_DEFS += -DHAL_USE_I2C=TRUE
COMMON_VPATH += $(DRIVER_PATH)/led/issi
SRC += is31fl3218.c
QUANTUM_LIB_SRC += i2c_master.c
endif
ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3731)
OPT_DEFS += -DIS31FL3731 -DSTM32_I2C -DHAL_USE_I2C=TRUE
OPT_DEFS += -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)
OPT_DEFS += -DIS31FL3733 -DSTM32_I2C -DHAL_USE_I2C=TRUE
OPT_DEFS += -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)
OPT_DEFS += -DIS31FL3736 -DSTM32_I2C -DHAL_USE_I2C=TRUE
OPT_DEFS += -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)
OPT_DEFS += -DIS31FL3737 -DSTM32_I2C -DHAL_USE_I2C=TRUE
OPT_DEFS += -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)
OPT_DEFS += -DIS31FL3741 -DSTM32_I2C -DHAL_USE_I2C=TRUE
OPT_DEFS += -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)
OPT_DEFS += -DIS31FLCOMMON -DIS31FL3742A -DSTM32_I2C -DHAL_USE_I2C=TRUE
ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3742a)
OPT_DEFS += -DIS31FLCOMMON -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)
OPT_DEFS += -DIS31FLCOMMON -DIS31FL3743A -DSTM32_I2C -DHAL_USE_I2C=TRUE
ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3743a)
OPT_DEFS += -DIS31FLCOMMON -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)
OPT_DEFS += -DIS31FLCOMMON -DIS31FL3745 -DSTM32_I2C -DHAL_USE_I2C=TRUE
ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3745)
OPT_DEFS += -DIS31FLCOMMON -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)
OPT_DEFS += -DIS31FLCOMMON -DIS31FL3746A -DSTM32_I2C -DHAL_USE_I2C=TRUE
ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3746a)
OPT_DEFS += -DIS31FLCOMMON -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)
OPT_DEFS += -DCKLED2001 -DSTM32_I2C -DHAL_USE_I2C=TRUE
ifeq ($(strip $(RGB_MATRIX_DRIVER)), snled27351)
OPT_DEFS += -DHAL_USE_I2C=TRUE
COMMON_VPATH += $(DRIVER_PATH)/led
SRC += ckled2001.c
SRC += snled27351.c
QUANTUM_LIB_SRC += i2c_master.c
endif
ifeq ($(strip $(RGB_MATRIX_DRIVER)), ws2812)
OPT_DEFS += -DWS2812
WS2812_DRIVER_REQUIRED := yes
endif
ifeq ($(strip $(RGB_MATRIX_DRIVER)), apa102)
OPT_DEFS += -DAPA102
APA102_DRIVER_REQUIRED := yes
endif
@ -567,6 +625,7 @@ ifeq ($(strip $(BACKLIGHT_ENABLE)), yes)
SRC += $(QUANTUM_DIR)/backlight/backlight.c
SRC += $(QUANTUM_DIR)/process_keycode/process_backlight.c
OPT_DEFS += -DBACKLIGHT_ENABLE
OPT_DEFS += -DBACKLIGHT_$(strip $(shell echo $(BACKLIGHT_DRIVER) | tr '[:lower:]' '[:upper:]'))
ifneq ($(strip $(BACKLIGHT_DRIVER)), custom)
SRC += $(QUANTUM_DIR)/backlight/backlight_driver_common.c
@ -587,7 +646,7 @@ ifeq ($(strip $(WS2812_DRIVER_REQUIRED)), yes)
$(call CATASTROPHIC_ERROR,Invalid WS2812_DRIVER,WS2812_DRIVER="$(WS2812_DRIVER)" is not a valid WS2812 driver)
endif
OPT_DEFS += -DWS2812_DRIVER_$(strip $(shell echo $(WS2812_DRIVER) | tr '[:lower:]' '[:upper:]'))
OPT_DEFS += -DWS2812_$(strip $(shell echo $(WS2812_DRIVER) | tr '[:lower:]' '[:upper:]'))
SRC += ws2812_$(strip $(WS2812_DRIVER)).c
@ -724,17 +783,16 @@ 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
OPT_DEFS += -DHAPTIC_$(strip $(shell echo $(HAPTIC_DRIVER) | tr '[:lower:]' '[:upper:]'))
COMMON_VPATH += $(DRIVER_PATH)/haptic
ifeq ($(strip $(HAPTIC_DRIVER)), drv2605l)
SRC += drv2605l.c
QUANTUM_LIB_SRC += i2c_master.c
OPT_DEFS += -DHAPTIC_DRV2605L
endif
ifeq ($(strip $(HAPTIC_DRIVER)), solenoid)
SRC += solenoid.c
OPT_DEFS += -DHAPTIC_SOLENOID
endif
endif
endif
@ -757,6 +815,7 @@ ifeq ($(strip $(OLED_ENABLE)), yes)
$(call CATASTROPHIC_ERROR,Invalid OLED_TRANSPORT,OLED_TRANSPORT="$(OLED_TRANSPORT)" is not a valid OLED transport)
else
OPT_DEFS += -DOLED_ENABLE
OPT_DEFS += -DOLED_$(strip $(shell echo $(OLED_DRIVER) | tr '[:lower:]' '[:upper:]'))
COMMON_VPATH += $(DRIVER_PATH)/oled
ifneq ($(strip $(OLED_DRIVER)), custom)
SRC += oled_driver.c
@ -784,13 +843,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)
@ -907,12 +968,13 @@ ifeq ($(strip $(BLUETOOTH_ENABLE)), yes)
$(call CATASTROPHIC_ERROR,Invalid BLUETOOTH_DRIVER,BLUETOOTH_DRIVER="$(BLUETOOTH_DRIVER)" is not a valid Bluetooth driver type)
endif
OPT_DEFS += -DBLUETOOTH_ENABLE
OPT_DEFS += -DBLUETOOTH_$(strip $(shell echo $(BLUETOOTH_DRIVER) | tr '[:lower:]' '[:upper:]'))
NO_USB_STARTUP_CHECK := yes
COMMON_VPATH += $(DRIVER_PATH)/bluetooth
SRC += outputselect.c
ifeq ($(strip $(BLUETOOTH_DRIVER)), bluefruit_le)
OPT_DEFS += -DBLUETOOTH_BLUEFRUIT_LE -DHAL_USE_SPI=TRUE
OPT_DEFS += -DHAL_USE_SPI=TRUE
SRC += $(DRIVER_PATH)/bluetooth/bluetooth.c
SRC += $(DRIVER_PATH)/bluetooth/bluefruit_le.cpp
QUANTUM_LIB_SRC += analog.c
@ -920,7 +982,7 @@ ifeq ($(strip $(BLUETOOTH_ENABLE)), yes)
endif
ifeq ($(strip $(BLUETOOTH_DRIVER)), rn42)
OPT_DEFS += -DBLUETOOTH_RN42 -DHAL_USE_SERIAL=TRUE
OPT_DEFS += -DHAL_USE_SERIAL=TRUE
SRC += $(DRIVER_PATH)/bluetooth/bluetooth.c
SRC += $(DRIVER_PATH)/bluetooth/rn42.c
QUANTUM_LIB_SRC += uart.c

View File

@ -12,6 +12,9 @@ vpath %.hpp $(VPATH_SRC)
vpath %.S $(VPATH_SRC)
VPATH :=
# Helper to return the distinct elements of a list
uniq = $(if $1,$(firstword $1) $(call uniq,$(filter-out $(firstword $1),$1)))
# Convert all SRC to OBJ
define OBJ_FROM_SRC
$(patsubst %.c,$1/%.o,$(patsubst %.cpp,$1/%.o,$(patsubst %.cc,$1/%.o,$(patsubst %.S,$1/%.o,$(patsubst %.clib,$1/%.a,$($1_SRC))))))
@ -264,7 +267,7 @@ BEGIN = gccversion sizebefore
# Note the obj.txt depeendency is there to force linking if a source file is deleted
%.elf: $(OBJ) $(MASTER_OUTPUT)/cflags.txt $(MASTER_OUTPUT)/ldflags.txt $(MASTER_OUTPUT)/obj.txt | $(BEGIN)
@$(SILENT) || printf "$(MSG_LINKING) $@" | $(AWK_CMD)
$(eval CMD=MAKE=$(MAKE) $(CC) $(ALL_CFLAGS) $(filter-out %.txt,$^) --output $@ $(LDFLAGS))
$(eval CMD=MAKE=$(MAKE) $(CC) $(ALL_CFLAGS) $(call uniq,$(OBJ)) --output $@ $(LDFLAGS))
@$(BUILD_CMD)
@ -380,31 +383,9 @@ dump_vars:
objs-size:
for i in $(OBJ); do echo $$i; done | sort | xargs $(SIZE)
ifeq ($(findstring avr-gcc,$(CC)),avr-gcc)
SIZE_MARGIN = 1024
# size check optionally implemented in its platform.mk
check-size:
$(eval MAX_SIZE=$(shell n=`$(CC) -E -mmcu=$(MCU) -D__ASSEMBLER__ $(CFLAGS) $(OPT_DEFS) platforms/avr/bootloader_size.c 2> /dev/null | $(SED) -ne 's/\r//;/^#/n;/^AVR_SIZE:/,$${s/^AVR_SIZE: //;p;}'` && echo $$(($$n)) || echo 0))
$(eval CURRENT_SIZE=$(shell if [ -f $(BUILD_DIR)/$(TARGET).hex ]; then $(SIZE) --target=$(FORMAT) $(BUILD_DIR)/$(TARGET).hex | $(AWK) 'NR==2 {print $$4}'; else printf 0; fi))
$(eval FREE_SIZE=$(shell expr $(MAX_SIZE) - $(CURRENT_SIZE)))
$(eval OVER_SIZE=$(shell expr $(CURRENT_SIZE) - $(MAX_SIZE)))
$(eval PERCENT_SIZE=$(shell expr $(CURRENT_SIZE) \* 100 / $(MAX_SIZE)))
if [ $(MAX_SIZE) -gt 0 ] && [ $(CURRENT_SIZE) -gt 0 ]; then \
$(SILENT) || printf "$(MSG_CHECK_FILESIZE)" | $(AWK_CMD); \
if [ $(CURRENT_SIZE) -gt $(MAX_SIZE) ]; then \
printf "\n * $(MSG_FILE_TOO_BIG)"; $(PRINT_ERROR_PLAIN); \
else \
if [ $(FREE_SIZE) -lt $(SIZE_MARGIN) ]; then \
$(PRINT_WARNING_PLAIN); printf " * $(MSG_FILE_NEAR_LIMIT)"; \
else \
$(PRINT_OK); $(SILENT) || printf " * $(MSG_FILE_JUST_RIGHT)"; \
fi ; \
fi ; \
fi
else
check-size:
$(SILENT) || echo "$(MSG_CHECK_FILESIZE_SKIPPED)"
endif
check-md5:
$(MD5SUM) $(BUILD_DIR)/$(TARGET).$(FIRMWARE_FORMAT)

View File

@ -37,7 +37,6 @@ GENERIC_FEATURES = \
SPACE_CADET \
SWAP_HANDS \
TAP_DANCE \
VELOCIKEY \
WPM \
DYNAMIC_TAPPING_TERM \
TRI_LAYER

View File

@ -91,7 +91,6 @@ MSG_AVAILABLE_KEYMAPS = $(eval $(call GENERATE_MSG_AVAILABLE_KEYMAPS))$(MSG_AVAI
MSG_BOOTLOADER_NOT_FOUND_BASE = Bootloader not found. Make sure the board is in bootloader mode. See https://docs.qmk.fm/\#/newbs_flashing\n
MSG_CHECK_FILESIZE = Checking file size of $(TARGET).$(FIRMWARE_FORMAT)
MSG_CHECK_FILESIZE_SKIPPED = (Firmware size check does not yet support $(MCU_ORIG); skipping)
MSG_FILE_TOO_BIG = $(ERROR_COLOR)The firmware is too large!$(NO_COLOR) $(CURRENT_SIZE)/$(MAX_SIZE) ($(OVER_SIZE) bytes over)\n
MSG_FILE_TOO_SMALL = The firmware is too small! $(CURRENT_SIZE)/$(MAX_SIZE)\n
MSG_FILE_JUST_RIGHT = The firmware size is fine - $(CURRENT_SIZE)/$(MAX_SIZE) ($(PERCENT_SIZE)%%, $(FREE_SIZE) bytes free)\n
@ -104,6 +103,10 @@ MSG_BOOTLOADER_NOT_FOUND = $(ERROR_COLOR)ERROR:$(NO_COLOR) $(MSG_BOOTLOADER_NOT_
BOOTLOADER_RETRY_TIME ?= 0.5
MSG_BOOTLOADER_NOT_FOUND_QUICK_RETRY = $(MSG_BOOTLOADER_NOT_FOUND_BASE) Trying again every $(BOOTLOADER_RETRY_TIME)s (Ctrl+C to cancel)
define WARNING_MESSAGE
$(shell printf "\n %-99s $(WARN_STRING)\n" "$1" >&2)
endef
define CATASTROPHIC_ERROR
$(shell printf "\n * %-99s $(ERROR_STRING)\n" "$2" >&2)
$(error $1)

View File

@ -49,6 +49,10 @@
"DYNAMIC_KEYMAP_EEPROM_MAX_ADDR": {"info_key": "dynamic_keymap.eeprom_max_addr", "value_type": "int"},
"DYNAMIC_KEYMAP_LAYER_COUNT": {"info_key": "dynamic_keymap.layer_count", "value_type": "int"},
// EEPROM
"WEAR_LEVELING_BACKING_SIZE": {"info_key": "eeprom.wear_leveling.backing_size", "value_type": "int", "to_json": false},
"WEAR_LEVELING_LOGICAL_SIZE": {"info_key": "eeprom.wear_leveling.logical_size", "value_type": "int", "to_json": false},
// Indicators
"LED_CAPS_LOCK_PIN": {"info_key": "indicators.caps_lock"},
"LED_NUM_LOCK_PIN": {"info_key": "indicators.num_lock"},
@ -63,12 +67,17 @@
"LEADER_TIMEOUT": {"info_key": "leader_key.timeout", "value_type": "int"},
// LED Matrix
"LED_DISABLE_WHEN_USB_SUSPENDED": {"info_key": "led_matrix.sleep", "value_type": "bool"},
"LED_MATRIX_CENTER": {"info_key": "led_matrix.center_point", "value_type": "array.int"},
"LED_MATRIX_KEYRELEASES": {"info_key": "led_matrix.react_on_keyup", "value_type": "bool"},
"LED_MATRIX_LED_FLUSH_LIMIT": {"info_key": "led_matrix.led_flush_limit", "value_type": "int"},
"LED_MATRIX_LED_PROCESS_LIMIT": {"info_key": "led_matrix.led_process_limit", "value_type": "int", "to_json": false},
"LED_MATRIX_MAXIMUM_BRIGHTNESS": {"info_key": "led_matrix.max_brightness", "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"},
@ -99,14 +108,19 @@
"PS2_DATA_PIN": {"info_key": "ps2.data_pin"},
// RGB Matrix
"RGB_DISABLE_WHEN_USB_SUSPENDED": {"info_key": "rgb_matrix.sleep", "value_type": "bool"},
"RGB_MATRIX_CENTER": {"info_key": "rgb_matrix.center_point", "value_type": "array.int"},
"RGB_MATRIX_HUE_STEP": {"info_key": "rgb_matrix.hue_steps", "value_type": "int"},
"RGB_MATRIX_KEYRELEASES": {"info_key": "rgb_matrix.react_on_keyup", "value_type": "bool"},
"RGB_MATRIX_LED_FLUSH_LIMIT": {"info_key": "rgb_matrix.led_flush_limit", "value_type": "int"},
"RGB_MATRIX_LED_PROCESS_LIMIT": {"info_key": "rgb_matrix.led_process_limit", "value_type": "int", "to_json": false},
"RGB_MATRIX_MAXIMUM_BRIGHTNESS": {"info_key": "rgb_matrix.max_brightness", "value_type": "int"},
"RGB_MATRIX_SAT_STEP": {"info_key": "rgb_matrix.sat_steps", "value_type": "int"},
"RGB_MATRIX_SPD_STEP": {"info_key": "rgb_matrix.speed_steps", "value_type": "int"},
"RGB_MATRIX_SPLIT": {"info_key": "rgb_matrix.split_count", "value_type": "array.int"},
"RGB_MATRIX_TIMEOUT": {"info_key": "rgb_matrix.timeout", "value_type": "int"},
"RGB_MATRIX_VAL_STEP": {"info_key": "rgb_matrix.val_steps", "value_type": "int"},
"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"},

View File

@ -42,6 +42,7 @@
"STENO_ENABLE": {"info_key": "stenography.enabled", "value_type": "bool"},
"STENO_PROTOCOL": {"info_key": "stenography.protocol"},
"WAIT_FOR_USB": {"info_key": "usb.wait_for", "value_type": "bool"},
"WEAR_LEVELING_DRIVER": {"info_key": "eeprom.wear_leveling.driver"},
"WS2812_DRIVER": {"info_key": "ws2812.driver"},
// Items we want flagged in lint

View File

@ -4,6 +4,13 @@
// "target": "<keyboard_folder>"
// }
//
/* This list of aliases is for testing purposes -- ensures "linked list" recursive traversal works correctly. */
"_test_a": { "target": "_test_b" },
"_test_b": { "target": "_test_c" },
"_test_c": { "target": "planck/rev6" },
/* The main list of aliases for moved keyboards within QMK. */
"2_milk": {
"target": "spaceman/2_milk"
},
@ -35,7 +42,7 @@
"target": "amjkeyboard/amjpad"
},
"angel64": {
"target": "angel64/alpha"
"target": "kakunpc/angel64/alpha"
},
"ashpil/modelm_usbc": {
"target": "ibm/model_m/ashpil_usbc"
@ -47,10 +54,10 @@
"target": "viktus/at101_bh"
},
"atom47/rev2": {
"target": "maartenwut/atom47/rev2"
"target": "evyd13/atom47/rev2"
},
"atom47/rev3": {
"target": "maartenwut/atom47/rev3"
"target": "evyd13/atom47/rev3"
},
"bakeneko60": {
"target": "kkatano/bakeneko60"
@ -65,7 +72,7 @@
"target": "bear_face/v1"
},
"bm16a": {
"target": "kprepublic/bm16a"
"target": "kprepublic/bm16a/v1"
},
"bm16s": {
"target": "kprepublic/bm16s"
@ -77,16 +84,16 @@
"target": "kprepublic/bm43a"
},
"bm60poker": {
"target": "kprepublic/bm60poker"
"target": "kprepublic/bm60hsrgb_poker/rev1"
},
"bm60rgb": {
"target": "kprepublic/bm60rgb"
"target": "kprepublic/bm60hsrgb/rev1"
},
"bm60rgb_iso": {
"target": "kprepublic/bm60rgb_iso"
"target": "kprepublic/bm60hsrgb_iso/rev1"
},
"bm68rgb": {
"target": "kprepublic/bm68rgb"
"target": "kprepublic/bm68hsrgb/rev1"
},
"bpiphany/pegasushoof": {
"target": "bpiphany/pegasushoof/2013"
@ -140,7 +147,10 @@
"target": "jagdpietr/drakon"
},
"durgod/k320": {
"target": "durgod/k3x0/k320"
"target": "durgod/k320/base"
},
"durgod/k3x0/k320": {
"target": "durgod/k320/base"
},
"durgod/hades": {
"target": "durgod/dgk6x/hades_ansi"
@ -166,6 +176,9 @@
"dztech/volcano660": {
"target": "ilumkb/volcano660"
},
"dztech/og60": {
"target": "dztech/tofu60"
},
"eek": {
"target": "eek/silk_down"
},
@ -199,6 +212,9 @@
"handwired/dactyl_manuform/6x6_kinesis": {
"target": "handwired/dactyl_kinesis"
},
"handwired/dactyl_manuform/dmote/62key": {
"target": "handwired/dmote"
},
"handwired/ferris": {
"target": "ferris/0_1"
},
@ -269,7 +285,7 @@
"target": "idb/idb_60"
},
"idobo": {
"target": "idobao/id75"
"target": "idobao/id75/v1"
},
"jacky_studio/piggy60": {
"target": "jacky_studio/piggy60/rev1"
@ -401,7 +417,7 @@
"target": "mechlovin/adelais/rgb_led/rev1"
},
"mechlovin/adelais/standard_led": {
"target": "mechlovin/adelais/standard_led/rev2"
"target": "mechlovin/adelais/standard_led/arm/rev2"
},
"mechlovin/delphine": {
"target": "mechlovin/delphine/mono_led"
@ -455,10 +471,10 @@
"target": "pabile/p20/ver1"
},
"pancake/feather": {
"target": "spaceman/pancake/feather"
"target": "spaceman/pancake/rev1/feather"
},
"pancake/promicro": {
"target": "spaceman/pancake/promicro"
"target": "spaceman/pancake/rev1/promicro"
},
"peiorisboards/ixora": {
"target": "coarse/ixora"
@ -467,7 +483,7 @@
"target": "dm9records/plaid"
},
"plain60": {
"target": "maartenwut/plain60"
"target": "evyd13/plain60"
},
"ploopyco/trackball": {
"target": "ploopyco/trackball/rev1_005"
@ -503,10 +519,10 @@
"target": "wilba_tech/rama_works_u80_a"
},
"ramonimbao/herringbone": {
"target": "ramonimbao/herringbone/v1"
"target": "rmi_kb/herringbone/v1"
},
"ramonimbao/mona": {
"target": "ramonimbao/mona/v1"
"target": "rmi_kb/mona/v1"
},
"rgbkb/pan": {
"target": "rgbkb/pan/rev1/32a"
@ -542,10 +558,10 @@
"target": "tkw/stoutgat/v1"
},
"suihankey": {
"target": "suihankey/split/alpha"
"target": "kakunpc/suihankey/split/alpha"
},
"ta65": {
"target": "maartenwut/ta65"
"target": "evyd13/ta65"
},
"tartan": {
"target": "dm9records/tartan"
@ -563,13 +579,13 @@
"target": "matthewdias/txuu"
},
"underscore33": {
"target": "underscore33/rev1"
"target": "tominabox1/underscore33/rev1"
},
"vinta": {
"target": "coarse/vinta"
},
"wasdat": {
"target": "maartenwut/wasdat"
"target": "evyd13/wasdat"
},
"westfoxtrot/cypher": {
"target": "westfoxtrot/cypher/rev1"
@ -581,10 +597,10 @@
"target": "xiudi/xd002"
},
"xd004": {
"target": "xiudi/xd004"
"target": "xiudi/xd004/v1"
},
"xd60": {
"target": "xiudi/xd60"
"target": "xiudi/xd60/rev2"
},
"xd68": {
"target": "xiudi/xd68"
@ -831,7 +847,7 @@
"target": "kagizaraya/halberd"
},
"handwired/hillside/0_1": {
"target": "handwired/hillside/48"
"target": "hillside/48/0_1"
},
"hecomi/alpha": {
"target": "takashiski/hecomi/alpha"
@ -843,10 +859,10 @@
"target": "bpiphany/hid_liber"
},
"id67/default_rgb": {
"target": "idobao/id67/default_rgb"
"target": "idobao/id67"
},
"id67/rgb": {
"target": "idobao/id67/rgb"
"target": "idobao/id67"
},
"id80": {
"target": "idobao/id80/v2/ansi"
@ -884,6 +900,18 @@
"kelowna/rgb64": {
"target": "weirdo/kelowna/rgb64"
},
"keychron/q0": {
"target": "keychron/q0/base"
},
"keychron/q1": {
"target": "keychron/q1v1"
}
"keychron/q4": {
"target": "keychron/q4/ansi"
}
"kprepublic/bm40hsrgb": {
"target": "kprepublic/bm40hsrgb/rev1"
},
"kprepublic/bm65hsrgb_iso": {
"target": "kprepublic/bm65hsrgb_iso/rev1"
},
@ -1236,7 +1264,7 @@
"target": "marksard/treadstone48/rev2"
},
"tronguylabs/m122_3270": {
"target": "ibm/model_m_122/m122_3270"
"target": "ibm/model_m_122/m122_3270/teensy"
},
"ua62": {
"target": "nacly/ua62"
@ -1290,7 +1318,7 @@
"target": "ydkb/yd68"
},
"ymd75": {
"target": "ymdk/ymd75"
"target": "ymdk/ymd75/rev1"
},
"ymd96": {
"target": "ymdk/ymd96"
@ -1312,5 +1340,9 @@
},
"zinc/reva": {
"target": "25keys/zinc/reva"
},
// Moved during 2023 Q4 cycle
"ymdk/melody96": {
"target": "ymdk/melody96/soldered"
}
}

View File

@ -29,6 +29,10 @@
"minLength": 1,
"maxLength": 250
},
"snake_case": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]*$"
},
"layout_macro": {
"oneOf": [
{

View File

@ -176,6 +176,7 @@
"bootloader": {
"type": "string",
"enum": [
"apm32-dfu",
"atmel-dfu",
"bootloadhid",
"caterina",
@ -246,7 +247,19 @@
},
"eeprom": {
"properties": {
"driver": {"type": "string"}
"driver": {"type": "string"},
"wear_leveling": {
"type": "object",
"additionalProperties": false,
"properties": {
"driver": {
"type": "string",
"enum": ["custom", "embedded_flash", "legacy", "rp2040_flash", "spi_flash"]
},
"backing_size": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"logical_size": {"$ref": "qmk.definitions.v1#/unsigned_int"}
}
}
}
},
"encoder": {
@ -255,7 +268,11 @@
"enabled": {"type": "boolean"}
}
},
"features": {"$ref": "qmk.definitions.v1#/boolean_array"},
"features": {
"$ref": "qmk.definitions.v1#/boolean_array",
"propertyNames": { "$ref": "qmk.definitions.v1#/snake_case" }
},
"indicators": {
"type": "object",
"properties": {
@ -369,6 +386,7 @@
"properties": {
"animations": {
"type": "object",
"propertyNames": { "$ref": "qmk.definitions.v1#/snake_case" }
"additionalProperties": {
"type": "boolean"
}
@ -384,6 +402,10 @@
"timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"val_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"speed_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"led_flush_limit": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"led_process_limit": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"react_on_keyup": {"type": "boolean"},
"sleep": {"type": "boolean"},
"split_count": {
"type": "array",
"minItems": 2,
@ -418,6 +440,7 @@
"properties": {
"animations": {
"type": "object",
"propertyNames": { "$ref": "qmk.definitions.v1#/snake_case" }
"additionalProperties": {
"type": "boolean"
}
@ -435,6 +458,10 @@
"sat_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"val_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"speed_steps": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"led_flush_limit": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"led_process_limit": {"$ref": "qmk.definitions.v1#/unsigned_int"},
"react_on_keyup": {"type": "boolean"},
"sleep": {"type": "boolean"},
"split_count": {
"type": "array",
"minItems": 2,
@ -470,6 +497,7 @@
"properties": {
"animations": {
"type": "object",
"propertyNames": { "$ref": "qmk.definitions.v1#/snake_case" }
"additionalProperties": {
"type": "boolean"
}

305
docs/ChangeLog/20230827.md Normal file
View 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))

View File

@ -80,6 +80,7 @@
* [Caps Word](feature_caps_word.md)
* [Combos](feature_combo.md)
* [Debounce API](feature_debounce_type.md)
* [Digitizer](feature_digitizer.md)
* [EEPROM](feature_eeprom.md)
* [Key Lock](feature_key_lock.md)
* [Key Overrides](feature_key_overrides.md)
@ -115,7 +116,6 @@
* [Bootmagic Lite](feature_bootmagic.md)
* [Converters](feature_converters.md)
* [Custom Matrix](custom_matrix.md)
* [Digitizer](feature_digitizer.md)
* [DIP Switch](feature_dip_switch.md)
* [Encoders](feature_encoders.md)
* [Haptic Feedback](feature_haptic_feedback.md)
@ -126,7 +126,6 @@
* [PS/2 Mouse](feature_ps2_mouse.md)
* [Split Keyboard](feature_split_keyboard.md)
* [Stenography](feature_stenography.md)
* [Velocikey](feature_velocikey.md)
* Keyboard Building
* [Easy Maker for One Offs](easy_maker.md)
@ -139,7 +138,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
@ -148,6 +147,7 @@
* [Compatible Microcontrollers](compatible_microcontrollers.md)
* [Drivers](hardware_drivers.md)
* [ADC Driver](adc_driver.md)
* [APA102 Driver](apa102_driver.md)
* [Audio Driver](audio_driver.md)
* [I2C Driver](i2c_driver.md)
* [SPI Driver](spi_driver.md)

49
docs/apa102_driver.md Normal file
View File

@ -0,0 +1,49 @@
# APA102 Driver :id=apa102-driver
This driver provides support for APA102 addressable RGB LEDs. They are similar to the [WS2812](ws2812_driver.md) LEDs, but have increased data and refresh rates.
## Usage :id=usage
In most cases, the APA102 driver code is automatically included if you are using either the [RGBLight](feature_rgblight.md) or [RGB Matrix](feature_rgb_matrix.md) feature with the `apa102` driver set, and you would use those APIs instead.
However, if you need to use the driver standalone, add the following to your `rules.mk`:
```make
APA102_DRIVER_REQUIRED = yes
```
You can then call the APA102 API by including `apa102.h` in your code.
## Basic Configuration :id=basic-configuration
Add the following to your `config.h`:
|Define |Default |Description |
|---------------------------|-------------|------------------------------------------------------------------|
|`APA102_DI_PIN` |*Not defined*|The GPIO pin connected to the DI pin of the first LED in the chain|
|`APA102_CI_PIN` |*Not defined*|The GPIO pin connected to the CI pin of the first LED in the chain|
|`APA102_DEFAULT_BRIGHTNESS`|`31` |The default global brightness level of the LEDs, from 0 to 31 |
## API :id=api
### `void apa102_setleds(rgb_led_t *start_led, uint16_t num_leds)`
Send RGB data to the APA102 LED chain.
#### Arguments :id=api-apa102-setleds-arguments
- `rgb_led_t *start_led`
A pointer to the LED array.
- `uint16_t num_leds`
The length of the LED array.
---
### `void apa102_set_brightness(uint8_t brightness)`
Set the global brightness.
#### Arguments :id=api-apa102-set-brightness-arguments
- `uint8_t brightness`
The brightness level to set, from 0 to 31.

View File

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

View File

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

View File

@ -6,7 +6,7 @@ The QMK CLI makes building and working with QMK keyboards easier. We have provid
### Requirements :id=requirements
QMK requires Python 3.6 or greater. We try to keep the number of requirements small but you will also need to install the packages listed in [`requirements.txt`](https://github.com/qmk/qmk_firmware/blob/master/requirements.txt). These are installed automatically when you install the QMK CLI.
QMK requires Python 3.7 or greater. We try to keep the number of requirements small but you will also need to install the packages listed in [`requirements.txt`](https://github.com/qmk/qmk_firmware/blob/master/requirements.txt). These are installed automatically when you install the QMK CLI.
### Install Using Homebrew (macOS, some Linux) :id=install-using-homebrew
@ -20,7 +20,7 @@ qmk setup # This will clone `qmk/qmk_firmware` and optionally set up your build
### Install Using pip :id=install-using-easy_install-or-pip
If your system is not listed above you can install QMK manually. First ensure that you have Python 3.6 (or later) installed and have installed pip. Then install QMK with this command:
If your system is not listed above you can install QMK manually. First ensure that you have Python 3.7 (or later) installed and have installed pip. Then install QMK with this command:
```
python3 -m pip install qmk

View File

@ -44,7 +44,7 @@ def hello(cli):
First we import the `cli` object from `milc`. This is how we interact with the user and control the script's behavior. We use `@cli.argument()` to define a command line flag, `--name`. This also creates a configuration variable named `hello.name` (and the corresponding `user.name`) which the user can set so they don't have to specify the argument. The `cli.subcommand()` decorator designates this function as a subcommand. The name of the subcommand will be taken from the name of the function.
Once inside our function we find a typical "Hello, World!" program. We use `cli.log` to access the underlying [Logger Object](https://docs.python.org/3.6/library/logging.html#logger-objects), whose behavior is user controllable. We also access the value for name supplied by the user as `cli.config.hello.name`. The value for `cli.config.hello.name` will be determined by looking at the `--name` argument supplied by the user, if not provided it will use the value in the `qmk.ini` config file, and if neither of those is provided it will fall back to the default supplied in the `cli.argument()` decorator.
Once inside our function we find a typical "Hello, World!" program. We use `cli.log` to access the underlying [Logger Object](https://docs.python.org/3.7/library/logging.html#logger-objects), whose behavior is user controllable. We also access the value for name supplied by the user as `cli.config.hello.name`. The value for `cli.config.hello.name` will be determined by looking at the `--name` argument supplied by the user, if not provided it will use the value in the `qmk.ini` config file, and if neither of those is provided it will fall back to the default supplied in the `cli.argument()` decorator.
# User Interaction
@ -56,13 +56,13 @@ There are two main methods for outputting text in a subcommand- `cli.log` and `c
You can use special tokens to colorize your text, to make it easier to understand the output of your program. See [Colorizing Text](#colorizing-text) below.
Both of these methods support built-in string formatting using python's [printf style string format operations](https://docs.python.org/3.6/library/stdtypes.html#old-string-formatting). You can use tokens such as `%s` and `%d` within your text strings then pass the values as arguments. See our Hello, World program above for an example.
Both of these methods support built-in string formatting using python's [printf style string format operations](https://docs.python.org/3.7/library/stdtypes.html#old-string-formatting). You can use tokens such as `%s` and `%d` within your text strings then pass the values as arguments. See our Hello, World program above for an example.
You should never use the format operator (`%`) directly, always pass values as arguments.
### Logging (`cli.log`)
The `cli.log` object gives you access to a [Logger Object](https://docs.python.org/3.6/library/logging.html#logger-objects). We have configured our log output to show the user a nice emoji for each log level (or the log level name if their terminal does not support unicode.) This way the user can tell at a glance which messages are most important when something goes wrong.
The `cli.log` object gives you access to a [Logger Object](https://docs.python.org/3.7/library/logging.html#logger-objects). We have configured our log output to show the user a nice emoji for each log level (or the log level name if their terminal does not support unicode.) This way the user can tell at a glance which messages are most important when something goes wrong.
The default log level is `INFO`. If the user runs `qmk -v <subcommand>` the default log level will be set to `DEBUG`.

View File

@ -317,7 +317,7 @@ At the time of this writing our tests are not very comprehensive. Looking at the
## Integration Tests
Integration tests can be found in `lib/python/qmk/tests/test_cli_commands.py`. This is where CLI commands are actually run and their overall behavior is verified. We use [`subprocess`](https://docs.python.org/3.6/library/subprocess.html#module-subprocess) to launch each CLI command and a combination of checking output and returncode to determine if the right thing happened.
Integration tests can be found in `lib/python/qmk/tests/test_cli_commands.py`. This is where CLI commands are actually run and their overall behavior is verified. We use [`subprocess`](https://docs.python.org/3.7/library/subprocess.html#module-subprocess) to launch each CLI command and a combination of checking output and returncode to determine if the right thing happened.
## Unit Tests

View File

@ -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.
@ -67,7 +67,7 @@ We have a few different types of changes in QMK, each requiring a different leve
* 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:
@ -114,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.
@ -131,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)

View File

@ -105,11 +105,11 @@ Configurable options in your keyboard's `config.h`:
`config.h` override | Default | Description
-----------------------------------------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
`#define WEAR_LEVELING_EFL_FIRST_SECTOR` | _unset_ | The first sector on the MCU to use. By default this is not defined and calculated at runtime based on the MCU. However, different flash sizes on MCUs may require custom configuration.
`#define WEAR_LEVELING_EFL_FLASH_SIZE` | _unset_ | Allows overriding the flash size available for use for wear-leveling. Under normal circumstances this is automatically calculated and should not need to be overridden. Specifying a size larger than the amount actually available in flash will usually prevent the MCU from booting.
`#define WEAR_LEVELING_LOGICAL_SIZE` | `1024` | Number of bytes "exposed" to the rest of QMK and denotes the size of the usable EEPROM.
`#define WEAR_LEVELING_BACKING_SIZE` | `2048` | Number of bytes used by the wear-leveling algorithm for its underlying storage, and needs to be a multiple of the logical size.
`#define BACKING_STORE_WRITE_SIZE` | _automatic_ | The byte width of the underlying write used on the MCU, and is usually automatically determined from the selected MCU family. If an error occurs in the auto-detection, you'll need to consult the MCU's datasheet and determine this value, specifying it directly.
`#define WEAR_LEVELING_EFL_FIRST_SECTOR` | _unset_ | The first sector on the MCU to use. By default this is not defined and calculated at runtime based on the MCU. However, different flash sizes on MCUs may require custom configuration.
`#define WEAR_LEVELING_EFL_FLASH_SIZE` | _unset_ | Allows overriding the flash size available for use for wear-leveling. Under normal circumstances this is automatically calculated and should not need to be overridden. Specifying a size larger than the amount actually available in flash will usually prevent the MCU from booting.
`#define WEAR_LEVELING_LOGICAL_SIZE` | `(backing_size/2)` | Number of bytes "exposed" to the rest of QMK and denotes the size of the usable EEPROM.
`#define WEAR_LEVELING_BACKING_SIZE` | `2048` | Number of bytes used by the wear-leveling algorithm for its underlying storage, and needs to be a multiple of the logical size.
`#define BACKING_STORE_WRITE_SIZE` | _automatic_ | The byte width of the underlying write used on the MCU, and is usually automatically determined from the selected MCU family. If an error occurs in the auto-detection, you'll need to consult the MCU's datasheet and determine this value, specifying it directly.
!> If your MCU does not boot after swapping to the EFL wear-leveling driver, it's likely that the flash size is incorrectly detected, usually as an MCU with larger flash and may require overriding.
@ -139,7 +139,7 @@ Configurable options in your keyboard's `config.h`:
------------------------------------------|----------------------------|--------------------------------------------------------------------------------------------------------------------------------
`#define WEAR_LEVELING_RP2040_FLASH_SIZE` | `PICO_FLASH_SIZE_BYTES` | Number of bytes of flash on the board.
`#define WEAR_LEVELING_RP2040_FLASH_BASE` | `(flash_size-sector_size)` | The byte-wise location that the backing storage should be located.
`#define WEAR_LEVELING_LOGICAL_SIZE` | `4096` | Number of bytes "exposed" to the rest of QMK and denotes the size of the usable EEPROM.
`#define WEAR_LEVELING_LOGICAL_SIZE` | `(backing_size/2)` | Number of bytes "exposed" to the rest of QMK and denotes the size of the usable EEPROM.
`#define WEAR_LEVELING_BACKING_SIZE` | `8192` | Number of bytes used by the wear-leveling algorithm for its underlying storage, and needs to be a multiple of the logical size as well as the sector size.
`#define BACKING_STORE_WRITE_SIZE` | `2` | The write width used whenever a write is performed on the external flash peripheral.

View File

@ -180,18 +180,18 @@ For more granular control, there is `get_auto_shifted_key`. The default function
bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
# ifndef NO_AUTO_SHIFT_ALPHA
case KC_A ... KC_Z:
case AUTO_SHIFT_ALPHA:
# endif
# ifndef NO_AUTO_SHIFT_NUMERIC
case KC_1 ... KC_0:
case AUTO_SHIFT_NUMERIC:
# endif
# ifndef NO_AUTO_SHIFT_SPECIAL
# ifndef NO_AUTO_SHIFT_TAB
# ifndef NO_AUTO_SHIFT_TAB
case KC_TAB:
# endif
# ifndef NO_AUTO_SHIFT_SYMBOLS
# endif
# ifndef NO_AUTO_SHIFT_SYMBOLS
case AUTO_SHIFT_SYMBOLS:
# endif
# endif
# endif
# ifdef AUTO_SHIFT_ENTER
case KC_ENT:
@ -310,10 +310,16 @@ generating taps on release. For example:
#define RETRO_SHIFT 500
```
Without a value set, holds of any length without an interrupting key will produce the shifted value.
This value (if set) must be greater than one's `TAPPING_TERM`, as the key press
must be designated as a 'hold' by `process_tapping` before we send the modifier.
[Per-key tapping terms](tap_hold.md#tapping-term) can be used as a workaround.
There is no such limitation in regards to `AUTO_SHIFT_TIMEOUT` for normal keys.
**Note:** Tap Holds must be added to Auto Shift, see [here.](feature_auto_shift.md#auto-shift-per-key)
`IS_RETRO` may be helpful if one wants all Tap Holds retro shifted.
### Retro Shift and Tap Hold Configurations
Tap Hold Configurations work a little differently when using Retro Shift.

View File

@ -37,8 +37,10 @@ Add the following to your `config.h`:
|`BREATHING_PERIOD` |`6` |The length of one backlight "breath" in seconds |
|`BACKLIGHT_ON_STATE` |`1` |The state of the backlight pin when the backlight is "on" - `1` for high, `0` for low |
|`BACKLIGHT_LIMIT_VAL` |`255` |The maximum duty cycle of the backlight -- `255` allows for full brightness, any lower will decrease the maximum.|
|`BACKLIGHT_DEFAULT_ON` |`true` |Enable backlight upon clearing the EEPROM |
|`BACKLIGHT_DEFAULT_BREATHING`|`false` |Whether to enable backlight breathing upon clearing the EEPROM |
|`BACKLIGHT_DEFAULT_LEVEL` |`BACKLIGHT_LEVELS`|The default backlight level to use upon clearing the EEPROM |
|`BACKLIGHT_DEFAULT_BREATHING`|*Not defined* |Whether to enable backlight breathing upon clearing the EEPROM |
|`BACKLIGHT_PWM_PERIOD` |2048Hz |Defaults to `BACKLIGHT_PWM_COUNTER_FREQUENCY / 2048`, which results in a PWM frequency of 2048Hz. |
Unless you are designing your own keyboard, you generally should not need to change the `BACKLIGHT_PIN` or `BACKLIGHT_ON_STATE`.

View File

@ -19,9 +19,9 @@ You can use between 1 and 4 IS31FL3731 IC's. Do not specify `LED_DRIVER_ADDR_<N>
| Variable | Description | Default |
|----------|-------------|---------|
| `ISSI_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 |
| `ISSI_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 |
| `LED_DRIVER_COUNT` | (Required) How many LED driver IC's are present | |
| `IS31FL3731_I2C_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 |
| `IS31FL3731_I2C_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 |
| `IS31FL3731_DRIVER_COUNT` | (Required) How many LED driver IC's are present | |
| `LED_MATRIX_LED_COUNT` | (Required) How many LED lights are present across all drivers | |
| `LED_DRIVER_ADDR_1` | (Required) Address for the first LED driver | |
| `LED_DRIVER_ADDR_2` | (Optional) Address for the second LED driver | |
@ -34,14 +34,16 @@ Here is an example using 2 drivers.
// This is a 7-bit address, that gets left-shifted and bit 0
// set to 0 for write, 1 for read (as per I2C protocol)
// The address will vary depending on your wiring:
// 0b1110100 AD <-> GND
// 0b1110111 AD <-> VCC
// 0b1110101 AD <-> SCL
// 0b1110110 AD <-> SDA
#define LED_DRIVER_ADDR_1 0b1110100
#define LED_DRIVER_ADDR_2 0b1110110
// 00 AD <-> GND
// 01 AD <-> SCL
// 10 AD <-> SDA
// 11 AD <-> VCC
// ADDR represents A1:A0 of the 7-bit address.
// The result is: 0b11101(ADDR)
#define LED_DRIVER_ADDR_1 IS31FL3731_I2C_ADDRESS_GND
#define LED_DRIVER_ADDR_2 IS31FL3731_I2C_ADDRESS_SDA
#define LED_DRIVER_COUNT 2
#define IS31FL3731_DRIVER_COUNT 2
#define LED_DRIVER_1_LED_TOTAL 25
#define LED_DRIVER_2_LED_TOTAL 24
#define LED_MATRIX_LED_COUNT (LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL)
@ -49,12 +51,12 @@ Here is an example using 2 drivers.
!> Note the parentheses, this is so when `LED_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL)` will give very different results than `rand() % LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL`.
For split keyboards using `LED_MATRIX_SPLIT` with an LED driver, you can either have the same driver address or different driver addresses. If using different addresses, use `DRIVER_ADDR_1` for one and `DRIVER_ADDR_2` for the other one. Then, in `g_is31_leds`, fill out the correct driver index (0 or 1). If using one address, use `DRIVER_ADDR_1` for both, and use index 0 for `g_is31_leds`.
For split keyboards using `LED_MATRIX_SPLIT` with an LED driver, you can either have the same driver address or different driver addresses. If using different addresses, use `DRIVER_ADDR_1` for one and `DRIVER_ADDR_2` for the other one. Then, in `g_is31fl3731_leds`, fill out the correct driver index (0 or 1). If using one address, use `DRIVER_ADDR_1` for both, and use index 0 for `g_is31fl3731_leds`.
Define these arrays listing all the LEDs in your `<keyboard>.c`:
```c
const is31_led PROGMEM g_is31_leds[LED_MATRIX_LED_COUNT] = {
const is31fl3731_led_t PROGMEM g_is31fl3731_leds[LED_MATRIX_LED_COUNT] = {
/* Refer to IS31 manual for these locations
* driver
* | LED address
@ -159,7 +161,7 @@ Then Define the array listing all the LEDs you want to override in your `<keyboa
```c
const is31_led PROGMEM g_is31_scaling[ISSI_MANUAL_SCALING] = {
* LED Index
/* LED Index
* | Scaling
* | | */
{5, 120},
@ -248,16 +250,16 @@ enum led_matrix_effects {
LED_MATRIX_CYCLE_OUT_IN, // Full gradient scrolling out to in
LED_MATRIX_DUAL_BEACON, // Full gradient spinning around center of keyboard
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
LED_MATRIX_SOLID_REACTIVE_CROSS // Value pulses the same column and row of a single key hit then fades out
LED_MATRIX_SOLID_REACTIVE_MULTICROSS // Value pulses the same column and row of multiple key hits then fades out
LED_MATRIX_SOLID_REACTIVE_NEXUS // Value pulses away on the same column and row of a single key hit then fades out
LED_MATRIX_SOLID_REACTIVE_MULTINEXUS // Value pulses away on the same column and row of multiple key hits 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
LED_MATRIX_SOLID_REACTIVE_CROSS, // Value pulses the same column and row of a single key hit then fades out
LED_MATRIX_SOLID_REACTIVE_MULTICROSS, // Value pulses the same column and row of multiple key hits then fades out
LED_MATRIX_SOLID_REACTIVE_NEXUS, // Value pulses away on the same column and row of a single key hit then fades out
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
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_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
};
```
@ -363,6 +365,7 @@ For inspiration and examples, check out the built-in effects under `quantum/led_
#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)
#define LED_MATRIX_LED_FLUSH_LIMIT 16 // limits in milliseconds how frequently an animation will update the LEDs. 16 (16ms) is equivalent to limiting to 60fps (increases keyboard responsiveness)
#define LED_MATRIX_MAXIMUM_BRIGHTNESS 255 // limits maximum brightness of LEDs
#define LED_MATRIX_DEFAULT_ON true // Sets the default enabled state, if none has been set
#define LED_MATRIX_DEFAULT_MODE LED_MATRIX_SOLID // Sets the default mode, if none has been set
#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

View File

@ -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"]}
]
],

View File

@ -10,6 +10,7 @@ Tested combinations:
|SSD1306 |128x32 |AVR |Primary support |
|SSD1306 |128x64 |AVR |Verified working |
|SSD1306 |128x32 |Arm | |
|SSD1306 |128x64 |Arm |Verified working |
|SH1106 |128x64 |AVR |No scrolling |
|SH1107 |64x128 |AVR |No scrolling |
|SH1107 |64x128 |Arm |No scrolling |
@ -166,6 +167,42 @@ bool oled_task_user(void) {
#endif
```
Render a message before booting into bootloader mode.
```c
void oled_render_boot(bool bootloader) {
oled_clear();
for (int i = 0; i < 16; i++) {
oled_set_cursor(0, i);
if (bootloader) {
oled_write_P(PSTR("Awaiting New Firmware "), false);
} else {
oled_write_P(PSTR("Rebooting "), false);
}
}
oled_render_dirty(true);
}
bool reboot = false;
bool uint16_t keycode, keyrecord_t *record) {
if (record->event.pressed) {
// Display a special message prior to rebooting...
if (keycode == QK_BOOT) {
reboot = true;
}
}
return true;
}
void shutdown_user(void) {
oled_render_boot(reboot);
}
```
## Basic Configuration
These configuration options should be placed in `config.h`. Example:
@ -275,7 +312,7 @@ Rotation on SH1106 and SH1107 is noticeably less efficient than on SSD1306, beca
## OLED API
```c
// OLED rotation enum values are flags
// OLED Rotation enum values are flags
typedef enum {
OLED_ROTATION_0 = 0,
OLED_ROTATION_90 = 1,
@ -283,7 +320,7 @@ typedef enum {
OLED_ROTATION_270 = 3, // OLED_ROTATION_90 | OLED_ROTATION_180
} oled_rotation_t;
// Initialize the OLED display, rotating the rendered output based on the define passed in.
// Initialize the oled display, rotating the rendered output based on the define passed in.
// Returns true if the OLED was initialized successfully
bool oled_init(oled_rotation_t rotation);
@ -301,8 +338,12 @@ bool oled_send_data(const uint8_t *data, uint16_t size);
// Clears the display buffer, resets cursor position to 0, and sets the buffer to dirty for rendering
void oled_clear(void);
// Renders the dirty chunks of the buffer to OLED display
void oled_render(void);
// Alias to oled_render_dirty to avoid a change in api.
#define oled_render() oled_render_dirty(false)
// Renders all dirty blocks to the display at one time or a subset depending on the value of
// all.
void oled_render_dirty(bool all);
// Moves cursor to character position indicated by column and line, wraps if out of bounds
// Max column denoted by 'oled_max_chars()' and max lines by 'oled_max_lines()' functions
@ -333,8 +374,6 @@ void oled_write_ln(const char *data, bool invert);
// Pans the buffer to the right (or left by passing true) by moving contents of the buffer
// Useful for moving the screen in preparation for new drawing
// oled_scroll_left or oled_scroll_right should be preferred for all cases of moving a static
// image such as a logo or to avoid burn-in as it's much, much less cpu intensive
void oled_pan(bool left);
// Returns a pointer to the requested start index in the buffer plus remaining
@ -351,6 +390,7 @@ void oled_write_raw_byte(const char data, uint16_t index);
// Coordinates start at top-left and go right and down for positive x and y
void oled_write_pixel(uint8_t x, uint8_t y, bool on);
#if defined(__AVR__)
// Writes a PROGMEM string to the buffer at current cursor position
// Advances the cursor while writing, inverts the pixels if true
// Remapped to call 'void oled_write(const char *data, bool invert);' on ARM
@ -364,6 +404,11 @@ void oled_write_ln_P(const char *data, bool invert);
// Writes a PROGMEM string to the buffer at current cursor position
void oled_write_raw_P(const char *data, uint16_t size);
#else
# define oled_write_P(data, invert) oled_write(data, invert)
# define oled_write_ln_P(data, invert) oled_write_ln(data, invert)
# define oled_write_raw_P(data, size) oled_write_raw(data, size)
#endif // defined(__AVR__)
// Can be used to manually turn on the screen if it is off
// Returns true if the screen was on or turns on
@ -398,7 +443,7 @@ void oled_scroll_set_area(uint8_t start_line, uint8_t end_line);
// Sets scroll speed, 0-7, fastest to slowest. Default is three.
// Does not take effect until scrolling is either started or restarted
// the ssd1306 supports 8 speeds with the delay
// listed below betwen each frame of the scrolling effect
// listed below between each frame of the scrolling effect
// 0=2, 1=3, 2=4, 3=5, 4=25, 5=64, 6=128, 7=256
void oled_scroll_set_speed(uint8_t speed);

View File

@ -2,7 +2,7 @@
Programmable Buttons are keys that have no predefined meaning. This means they can be processed on the host side by custom software without the operating system trying to interpret them.
The keycodes are emitted according to the HID Telephony Device page (`0x0B`), Programmable Button usage (`0x07`). On Linux (> 5.14) they are handled automatically and translated to `KEY_MACRO#` keycodes (up to `KEY_MACRO30`).
The keycodes are emitted according to the HID Telephony Device page (`0x0B`), Programmable Button usage (`0x09`). On Linux (> 5.14) they are handled automatically and translated to `KEY_MACRO#` keycodes (up to `KEY_MACRO30`).
?> Currently there is no known support in Windows or macOS. It may be possible to write a custom HID driver to receive these usages, but this is out of the scope of the QMK documentation.

View File

@ -155,6 +155,29 @@ In your keyboard config.h:
#endif
```
### RP2040 PIO Version :id=rp2040-pio-version
The `PIO` subsystem is a Raspberry Pi RP2040 specific implementation, using the integrated PIO peripheral and is therefore only available on this MCU.
There are strict requirements for pin ordering but any pair of GPIO pins can be used. The GPIO used for clock must be directly after data, see the included info.json snippet for an example of correct order.
You may optionally switch the PIO peripheral used with the following define in config.h:
```c
#define PS2_PIO_USE_PIO1 // Force the usage of PIO1 peripheral, by default the PS2 implementation uses the PIO0 peripheral
```
Example info.json content:
```json
"ps2": {
"clock_pin": "GP1",
"data_pin": "GP0",
"driver": "vendor",
"enabled": true,
"mouse_enabled": true
}
```
## Additional Settings :id=additional-settings
### PS/2 Mouse Features :id=ps2-mouse-features

View File

@ -19,10 +19,10 @@ You can use between 1 and 4 IS31FL3731 IC's. Do not specify `DRIVER_ADDR_<N>` de
| Variable | Description | Default |
|----------|-------------|---------|
| `ISSI_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 |
| `ISSI_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 |
| `ISSI_3731_DEGHOST` | (Optional) Set this define to enable de-ghosting by halving Vcc during blanking time | |
| `DRIVER_COUNT` | (Required) How many RGB driver IC's are present | |
| `IS31FL3731_I2C_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 |
| `IS31FL3731_I2C_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 |
| `IS31FL3731_DEGHOST` | (Optional) Set this define to enable de-ghosting by halving Vcc during blanking time | |
| `IS31FL3731_DRIVER_COUNT` | (Required) How many RGB driver IC's are present | |
| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | |
| `DRIVER_ADDR_1` | (Required) Address for the first RGB driver | |
| `DRIVER_ADDR_2` | (Optional) Address for the second RGB driver | |
@ -35,14 +35,16 @@ Here is an example using 2 drivers.
// This is a 7-bit address, that gets left-shifted and bit 0
// set to 0 for write, 1 for read (as per I2C protocol)
// The address will vary depending on your wiring:
// 0b1110100 AD <-> GND
// 0b1110111 AD <-> VCC
// 0b1110101 AD <-> SCL
// 0b1110110 AD <-> SDA
#define DRIVER_ADDR_1 0b1110100
#define DRIVER_ADDR_2 0b1110110
// 00 AD <-> GND
// 01 AD <-> SCL
// 10 AD <-> SDA
// 11 AD <-> VCC
// ADDR represents A1:A0 of the 7-bit address.
// The result is: 0b11101(ADDR)
#define DRIVER_ADDR_1 IS31FL3731_I2C_ADDRESS_GND
#define DRIVER_ADDR_2 IS31FL3731_I2C_ADDRESS_SDA
#define DRIVER_COUNT 2
#define IS31FL3731_DRIVER_COUNT 2
#define DRIVER_1_LED_TOTAL 25
#define DRIVER_2_LED_TOTAL 24
#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
@ -50,12 +52,12 @@ Here is an example using 2 drivers.
!> Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`.
For split keyboards using `RGB_MATRIX_SPLIT` with an LED driver, you can either have the same driver address or different driver addresses. If using different addresses, use `DRIVER_ADDR_1` for one and `DRIVER_ADDR_2` for the other one. Then, in `g_is31_leds`, fill out the correct driver index (0 or 1). If using one address, use `DRIVER_ADDR_1` for both, and use index 0 for `g_is31_leds`.
For split keyboards using `RGB_MATRIX_SPLIT` with an LED driver, you can either have the same driver address or different driver addresses. If using different addresses, use `DRIVER_ADDR_1` for one and `DRIVER_ADDR_2` for the other one. Then, in `g_is31fl3731_leds`, fill out the correct driver index (0 or 1). If using one address, use `DRIVER_ADDR_1` for both, and use index 0 for `g_is31fl3731_leds`.
Define these arrays listing all the LEDs in your `<keyboard>.c`:
```c
const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT] = {
const is31fl3731_led_t PROGMEM g_is31fl3731_leds[RGB_MATRIX_LED_COUNT] = {
/* Refer to IS31 manual for these locations
* driver
* | R location
@ -83,13 +85,13 @@ You can use between 1 and 4 IS31FL3733 IC's. Do not specify `DRIVER_ADDR_<N>` de
| Variable | Description | Default |
|----------|-------------|---------|
| `ISSI_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 |
| `ISSI_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 |
| `ISSI_PWM_FREQUENCY` | (Optional) PWM Frequency Setting - IS31FL3733B only | 0 |
| `ISSI_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF |
| `ISSI_SWPULLUP` | (Optional) Set the value of the SWx lines on-chip de-ghosting resistors | PUR_0R (Disabled) |
| `ISSI_CSPULLUP` | (Optional) Set the value of the CSx lines on-chip de-ghosting resistors | PUR_0R (Disabled) |
| `DRIVER_COUNT` | (Required) How many RGB driver IC's are present | |
| `IS31FL3733_I2C_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 |
| `IS31FL3733_I2C_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 |
| `IS31FL3733_PWM_FREQUENCY` | (Optional) PWM Frequency Setting - IS31FL3733B only | 0 |
| `IS31FL3733_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF |
| `IS31FL3733_SWPULLUP` | (Optional) Set the value of the SWx lines on-chip de-ghosting resistors | PUR_0R (Disabled) |
| `IS31FL3733_CSPULLUP` | (Optional) Set the value of the CSx lines on-chip de-ghosting resistors | PUR_0R (Disabled) |
| `IS31FL3733_DRIVER_COUNT` | (Required) How many RGB driver IC's are present | |
| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | |
| `DRIVER_ADDR_1` | (Required) Address for the first RGB driver | |
| `DRIVER_ADDR_2` | (Optional) Address for the second RGB driver | |
@ -100,17 +102,17 @@ You can use between 1 and 4 IS31FL3733 IC's. Do not specify `DRIVER_ADDR_<N>` de
| `DRIVER_SYNC_3` | (Optional) Sync configuration for the third RGB driver | 0 |
| `DRIVER_SYNC_4` | (Optional) Sync configuration for the fourth RGB driver | 0 |
The IS31FL3733 IC's have on-chip resistors that can be enabled to allow for de-ghosting of the RGB matrix. By default these resistors are not enabled (`ISSI_SWPULLUP`/`ISSI_CSPULLUP` are given the value of`PUR_0R`), the values that can be set to enable de-ghosting are as follows:
The IS31FL3733 IC's have on-chip resistors that can be enabled to allow for de-ghosting of the RGB matrix. By default these resistors are not enabled (`IS31FL3733_SWPULLUP`/`IS31FL3733_CSPULLUP` are given the value of `IS31FL3733_PUR_0R`), the values that can be set to enable de-ghosting are as follows:
| `ISSI_SWPULLUP/ISSI_CSPULLUP` | Description |
| `IS31FL3733_SWPULLUP/IS31FL3733_CSPULLUP` | Description |
|----------------------|-------------|
| `PUR_0R` | (default) Do not use the on-chip resistors/enable de-ghosting |
| `PUR_05KR` | The 0.5k Ohm resistor used during blanking period (t_NOL) |
| `PUR_3KR` | The 3k Ohm resistor used at all times |
| `PUR_4KR` | The 4k Ohm resistor used at all times |
| `PUR_8KR` | The 8k Ohm resistor used at all times |
| `PUR_16KR` | The 16k Ohm resistor used at all times |
| `PUR_32KR` | The 32k Ohm resistor used during blanking period (t_NOL) |
| `IS31FL3733_PUR_0R` | (default) Do not use the on-chip resistors/enable de-ghosting |
| `IS31FL3733_PUR_05KR` | The 0.5k Ohm resistor used during blanking period (t_NOL) |
| `IS31FL3733_PUR_3KR` | The 3k Ohm resistor used at all times |
| `IS31FL3733_PUR_4KR` | The 4k Ohm resistor used at all times |
| `IS31FL3733_PUR_8KR` | The 8k Ohm resistor used at all times |
| `IS31FL3733_PUR_16KR` | The 16k Ohm resistor used at all times |
| `IS31FL3733_PUR_32KR` | The 32k Ohm resistor used during blanking period (t_NOL) |
Here is an example using 2 drivers.
@ -118,17 +120,17 @@ Here is an example using 2 drivers.
// This is a 7-bit address, that gets left-shifted and bit 0
// set to 0 for write, 1 for read (as per I2C protocol)
// The address will vary depending on your wiring:
// 00 <-> GND
// 01 <-> SCL
// 10 <-> SDA
// 11 <-> VCC
// 00 ADDRn <-> GND
// 01 ADDRn <-> SCL
// 10 ADDRn <-> SDA
// 11 ADDRn <-> VCC
// ADDR1 represents A1:A0 of the 7-bit address.
// ADDR2 represents A3:A2 of the 7-bit address.
// The result is: 0b101(ADDR2)(ADDR1)
#define DRIVER_ADDR_1 0b1010000
#define DRIVER_ADDR_2 0b1010011
#define DRIVER_ADDR_1 IS31FL3733_I2C_ADDRESS_GND_GND
#define DRIVER_ADDR_2 IS31FL3733_I2C_ADDRESS_GND_VCC
#define DRIVER_COUNT 2
#define IS31FL3733_DRIVER_COUNT 2
#define DRIVER_1_LED_TOTAL 58
#define DRIVER_2_LED_TOTAL 10
#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
@ -141,7 +143,7 @@ Currently only 4 drivers are supported, but it would be trivial to support all 8
Define these arrays listing all the LEDs in your `<keyboard>.c`:
```c
const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT] = {
const is31fl3733_led_t PROGMEM g_is31fl3733_leds[RGB_MATRIX_LED_COUNT] = {
/* Refer to IS31 manual for these locations
* driver
* | R location
@ -170,31 +172,31 @@ Configure the hardware via your `config.h`:
| Variable | Description | Default |
|----------|-------------|---------|
| `ISSI_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 |
| `ISSI_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 |
| `ISSI_PWM_FREQUENCY` | (Optional) PWM Frequency Setting - IS31FL3736B only | 0 |
| `ISSI_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF |
| `ISSI_SWPULLUP` | (Optional) Set the value of the SWx lines on-chip de-ghosting resistors | PUR_0R (Disabled) |
| `ISSI_CSPULLUP` | (Optional) Set the value of the CSx lines on-chip de-ghosting resistors | PUR_0R (Disabled) |
| `DRIVER_COUNT` | (Required) How many RGB driver IC's are present | |
| `IS31FL3736_I2C_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 |
| `IS31FL3736_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 |
| `IS31FL3736_PWM_FREQUENCY` | (Optional) PWM Frequency Setting - IS31FL3736B only | 0 |
| `IS31FL3736_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF |
| `IS31FL3736_SWPULLUP` | (Optional) Set the value of the SWx lines on-chip de-ghosting resistors | PUR_0R (Disabled) |
| `IS31FL3736_CSPULLUP` | (Optional) Set the value of the CSx lines on-chip de-ghosting resistors | PUR_0R (Disabled) |
| `IS31FL3736_DRIVER_COUNT` | (Required) How many RGB driver IC's are present | |
| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | |
| `DRIVER_ADDR_1` | (Required) Address for the first RGB driver | |
| `DRIVER_ADDR_2` | (Optional) Address for the second RGB driver | |
| `DRIVER_ADDR_3` | (Optional) Address for the third RGB driver | |
| `DRIVER_ADDR_4` | (Optional) Address for the fourth RGB driver | |
The IS31FL3736 IC's have on-chip resistors that can be enabled to allow for de-ghosting of the RGB matrix. By default these resistors are not enabled (`ISSI_SWPULLUP`/`ISSI_CSPULLUP` are given the value of`PUR_0R`), the values that can be set to enable de-ghosting are as follows:
The IS31FL3736 IC's have on-chip resistors that can be enabled to allow for de-ghosting of the RGB matrix. By default these resistors are not enabled (`IS31FL3736_SWPULLUP`/`IS31FL3736_CSPULLUP` are given the value of `IS31FL3736_PUR_0R`), the values that can be set to enable de-ghosting are as follows:
| `ISSI_SWPULLUP/ISSI_CSPULLUP` | Description |
| `IS31FL3736_SWPULLUP/IS31FL3736_CSPULLUP` | Description |
|----------------------|-------------|
| `PUR_0R` | (default) Do not use the on-chip resistors/enable de-ghosting |
| `PUR_05KR` | The 0.5k Ohm resistor used during blanking period (t_NOL) |
| `PUR_1KR` | The 1k Ohm resistor used during blanking period (t_NOL) |
| `PUR_2KR` | The 2k Ohm resistor used during blanking period (t_NOL) |
| `PUR_4KR` | The 4k Ohm resistor used during blanking period (t_NOL) |
| `PUR_8KR` | The 8k Ohm resistor during blanking period (t_NOL) |
| `PUR_16KR` | The 16k Ohm resistor during blanking period (t_NOL) |
| `PUR_32KR` | The 32k Ohm resistor used during blanking period (t_NOL) |
| `IS31FL3736_PUR_0R` | (default) Do not use the on-chip resistors/enable de-ghosting |
| `IS31FL3736_PUR_05KR` | The 0.5k Ohm resistor used during blanking period (t_NOL) |
| `IS31FL3736_PUR_1KR` | The 1k Ohm resistor used during blanking period (t_NOL) |
| `IS31FL3736_PUR_2KR` | The 2k Ohm resistor used during blanking period (t_NOL) |
| `IS31FL3736_PUR_4KR` | The 4k Ohm resistor used during blanking period (t_NOL) |
| `IS31FL3736_PUR_8KR` | The 8k Ohm resistor during blanking period (t_NOL) |
| `IS31FL3736_PUR_16KR` | The 16k Ohm resistor during blanking period (t_NOL) |
| `IS31FL3736_PUR_32KR` | The 32k Ohm resistor used during blanking period (t_NOL) |
Here is an example using 2 drivers.
@ -202,16 +204,17 @@ Here is an example using 2 drivers.
// This is a 7-bit address, that gets left-shifted and bit 0
// set to 0 for write, 1 for read (as per I2C protocol)
// The address will vary depending on your wiring:
// 0000 <-> GND
// 0101 <-> SCL
// 1010 <-> SDA
// 1111 <-> VCC
// ADDR represents A3:A0 of the 7-bit address.
// The result is: 0b101(ADDR)
#define DRIVER_ADDR_1 0b1010000
#define DRIVER_ADDR_2 0b1010001
// 00 ADDRn <-> GND
// 01 ADDRn <-> SCL
// 10 ADDRn <-> SDA
// 11 ADDRn <-> VCC
// ADDR1 represents A1:A0 of the 7-bit address.
// ADDR2 represents A3:A2 of the 7-bit address.
// The result is: 0b101(ADDR2)(ADDR1)
#define DRIVER_ADDR_1 IS31FL3736_I2C_ADDRESS_GND_GND
#define DRIVER_ADDR_2 IS31FL3736_I2C_ADDRESS_GND_SCL
#define DRIVER_COUNT 2
#define IS31FL3736_DRIVER_COUNT 2
#define DRIVER_1_LED_TOTAL 30
#define DRIVER_2_LED_TOTAL 32
#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
@ -221,7 +224,7 @@ Here is an example using 2 drivers.
Define these arrays listing all the LEDs in your `<keyboard>.c`:
```c
const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT] = {
const is31fl3736_led_t PROGMEM g_is31fl3736_leds[RGB_MATRIX_LED_COUNT] = {
/* Refer to IS31 manual for these locations
* driver
* | R location
@ -246,31 +249,31 @@ Configure the hardware via your `config.h`:
| Variable | Description | Default |
|----------|-------------|---------|
| `ISSI_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 |
| `ISSI_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 |
| `ISSI_PWM_FREQUENCY` | (Optional) PWM Frequency Setting - IS31FL3737B only | 0 |
| `ISSI_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF |
| `ISSI_SWPULLUP` | (Optional) Set the value of the SWx lines on-chip de-ghosting resistors | PUR_0R (Disabled) |
| `ISSI_CSPULLUP` | (Optional) Set the value of the CSx lines on-chip de-ghosting resistors | PUR_0R (Disabled) |
| `DRIVER_COUNT` | (Required) How many RGB driver IC's are present | |
| `IS31FL3737_I2C_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 |
| `IS31FL3737_I2C_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 |
| `IS31FL3737_PWM_FREQUENCY` | (Optional) PWM Frequency Setting - IS31FL3737B only | 0 |
| `IS31FL3737_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF |
| `IS31FL3737_SWPULLUP` | (Optional) Set the value of the SWx lines on-chip de-ghosting resistors | PUR_0R (Disabled) |
| `IS31FL3737_CSPULLUP` | (Optional) Set the value of the CSx lines on-chip de-ghosting resistors | PUR_0R (Disabled) |
| `IS31FL3737_DRIVER_COUNT` | (Required) How many RGB driver IC's are present | |
| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | |
| `DRIVER_ADDR_1` | (Required) Address for the first RGB driver | |
| `DRIVER_ADDR_2` | (Optional) Address for the second RGB driver | |
| `DRIVER_ADDR_3` | (Optional) Address for the third RGB driver | |
| `DRIVER_ADDR_4` | (Optional) Address for the fourth RGB driver | |
The IS31FL3737 IC's have on-chip resistors that can be enabled to allow for de-ghosting of the RGB matrix. By default these resistors are not enabled (`ISSI_SWPULLUP`/`ISSI_CSPULLUP` are given the value of`PUR_0R`), the values that can be set to enable de-ghosting are as follows:
The IS31FL3737 IC's have on-chip resistors that can be enabled to allow for de-ghosting of the RGB matrix. By default these resistors are not enabled (`IS31FL3737_SWPULLUP`/`IS31FL3737_CSPULLUP` are given the value of `IS31FL3737_PUR_0R`), the values that can be set to enable de-ghosting are as follows:
| `ISSI_SWPULLUP/ISSI_CSPULLUP` | Description |
| `IS31FL3737_SWPULLUP/IS31FL3737_CSPULLUP` | Description |
|----------------------|-------------|
| `PUR_0R` | (default) Do not use the on-chip resistors/enable de-ghosting |
| `PUR_05KR` | The 0.5k Ohm resistor used during blanking period (t_NOL) |
| `PUR_1KR` | The 1k Ohm resistor used during blanking period (t_NOL) |
| `PUR_2KR` | The 2k Ohm resistor used during blanking period (t_NOL) |
| `PUR_4KR` | The 4k Ohm resistor used during blanking period (t_NOL) |
| `PUR_8KR` | The 8k Ohm resistor during blanking period (t_NOL) |
| `PUR_16KR` | The 16k Ohm resistor during blanking period (t_NOL) |
| `PUR_32KR` | The 32k Ohm resistor used during blanking period (t_NOL) |
| `IS31FL3737_PUR_0R` | (default) Do not use the on-chip resistors/enable de-ghosting |
| `IS31FL3737_PUR_05KR` | The 0.5k Ohm resistor used during blanking period (t_NOL) |
| `IS31FL3737_PUR_1KR` | The 1k Ohm resistor used during blanking period (t_NOL) |
| `IS31FL3737_PUR_2KR` | The 2k Ohm resistor used during blanking period (t_NOL) |
| `IS31FL3737_PUR_4KR` | The 4k Ohm resistor used during blanking period (t_NOL) |
| `IS31FL3737_PUR_8KR` | The 8k Ohm resistor during blanking period (t_NOL) |
| `IS31FL3737_PUR_16KR` | The 16k Ohm resistor during blanking period (t_NOL) |
| `IS31FL3737_PUR_32KR` | The 32k Ohm resistor used during blanking period (t_NOL) |
Here is an example using 2 drivers.
@ -278,16 +281,16 @@ Here is an example using 2 drivers.
// This is a 7-bit address, that gets left-shifted and bit 0
// set to 0 for write, 1 for read (as per I2C protocol)
// The address will vary depending on your wiring:
// 0000 <-> GND
// 0101 <-> SCL
// 1010 <-> SDA
// 1111 <-> VCC
// 0000 ADDR <-> GND
// 0101 ADDR <-> SCL
// 1010 ADDR <-> SDA
// 1111 ADDR <-> VCC
// ADDR represents A3:A0 of the 7-bit address.
// The result is: 0b101(ADDR)
#define DRIVER_ADDR_1 0b1010000
#define DRIVER_ADDR_2 0b1010001
#define DRIVER_ADDR_1 IS31FL3737_I2C_ADDRESS_GND
#define DRIVER_ADDR_2 IS31FL3737_I2C_ADDRESS_SCL
#define DRIVER_COUNT 2
#define IS31FL3737_DRIVER_COUNT 2
#define DRIVER_1_LED_TOTAL 30
#define DRIVER_2_LED_TOTAL 36
#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
@ -297,7 +300,7 @@ Here is an example using 2 drivers.
Define these arrays listing all the LEDs in your `<keyboard>.c`:
```c
const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT] = {
const is31fl3737_led_t PROGMEM g_is31fl3737_leds[RGB_MATRIX_LED_COUNT] = {
/* Refer to IS31 manual for these locations
* driver
* | R location
@ -407,7 +410,7 @@ Then Define the array listing all the LEDs you want to override in your `<keyboa
```c
const is31_led PROGMEM g_is31_scaling[ISSI_MANUAL_SCALING] = {
* LED Index
/* LED Index
* | R scaling
* | | G scaling
* | | | B scaling
@ -465,41 +468,41 @@ Configure the hardware via your `config.h`:
```
---
### AW20216 :id=aw20216
There is basic support for addressable RGB matrix lighting with the SPI AW20216 RGB controller. To enable it, add this to your `rules.mk`:
### AW20216S :id=aw20216s
There is basic support for addressable RGB matrix lighting with the SPI AW20216S RGB controller. To enable it, add this to your `rules.mk`:
```make
RGB_MATRIX_ENABLE = yes
RGB_MATRIX_DRIVER = aw20216
RGB_MATRIX_DRIVER = aw20216s
```
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`:
You can use up to 2 AW20216S 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`:
| Variable | Description | Default |
|----------|-------------|---------|
| `DRIVER_1_CS` | (Required) MCU pin connected to first RGB driver chip select line | B13 |
| `DRIVER_2_CS` | (Optional) MCU pin connected to second RGB driver chip select line | |
| `DRIVER_1_EN` | (Required) MCU pin connected to first RGB driver hardware enable line | C13 |
| `DRIVER_2_EN` | (Optional) MCU pin connected to second RGB driver hardware enable line | |
| `AW20216S_DRIVER_1_CS` | (Required) MCU pin connected to first RGB driver chip select line | B13 |
| `AW20216S_DRIVER_2_CS` | (Optional) MCU pin connected to second RGB driver chip select line | |
| `AW20216S_DRIVER_1_EN` | (Required) MCU pin connected to first RGB driver hardware enable line | C13 |
| `AW20216S_DRIVER_2_EN` | (Optional) MCU pin connected to second RGB driver hardware enable line | |
| `DRIVER_1_LED_TOTAL` | (Required) How many RGB lights are connected to first RGB driver | |
| `DRIVER_2_LED_TOTAL` | (Optional) How many RGB lights are connected to second RGB driver | |
| `DRIVER_COUNT` | (Required) How many RGB driver IC's are present | |
| `AW20216S_DRIVER_COUNT` | (Required) How many RGB driver IC's are present | |
| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | |
| `AW_SCALING_MAX` | (Optional) LED current scaling value (0-255, higher values mean LED is brighter at full PWM) | 150 |
| `AW_GLOBAL_CURRENT_MAX` | (Optional) Driver global current limit (0-255, higher values means the driver may consume more power) | 150 |
| `AW_SPI_MODE` | (Optional) Mode for SPI communication (0-3, defines polarity and phase of the clock) | 3 |
| `AW_SPI_DIVISOR` | (Optional) Clock divisor for SPI communication (powers of 2, smaller numbers means faster communication, should not be less than 4) | 4 |
| `AW20216S_SCALING_MAX` | (Optional) LED current scaling value (0-255, higher values mean LED is brighter at full PWM) | 150 |
| `AW20216S_GLOBAL_CURRENT_MAX` | (Optional) Driver global current limit (0-255, higher values means the driver may consume more power) | 150 |
| `AW20216S_SPI_MODE` | (Optional) Mode for SPI communication (0-3, defines polarity and phase of the clock) | 3 |
| `AW20216S_SPI_DIVISOR` | (Optional) Clock divisor for SPI communication (powers of 2, smaller numbers means faster communication, should not be less than 4) | 4 |
Here is an example using 2 drivers.
```c
#define DRIVER_1_CS B13
#define DRIVER_2_CS B14
#define AW20216S_DRIVER_1_CS B13
#define AW20216S_DRIVER_2_CS B14
// Hardware enable lines may be connected to the same pin
#define DRIVER_1_EN C13
#define DRIVER_2_EN C13
#define AW20216S_DRIVER_1_EN C13
#define AW20216S_DRIVER_2_EN C13
#define DRIVER_COUNT 2
#define AW20216S_DRIVER_COUNT 2
#define DRIVER_1_LED_TOTAL 66
#define DRIVER_2_LED_TOTAL 32
#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
@ -510,10 +513,10 @@ Here is an example using 2 drivers.
Define these arrays listing all the LEDs in your `<keyboard>.c`:
```c
const aw_led PROGMEM g_aw_leds[RGB_MATRIX_LED_COUNT] = {
/* Each AW20216 channel is controlled by a register at some offset between 0x00
const aw20216s_led_t PROGMEM g_aw20216s_leds[RGB_MATRIX_LED_COUNT] = {
/* Each AW20216S channel is controlled by a register at some offset between 0x00
* and 0xD7 inclusive.
* See drivers/awinic/aw20216.h for the mapping between register offsets and
* See drivers/led/aw20216s.h for the mapping between register offsets and
* driver pin locations.
* driver
* | R location
@ -642,6 +645,7 @@ enum rgb_matrix_effects {
RGB_MATRIX_DUAL_BEACON, // Full gradient spinning around center of keyboard
RGB_MATRIX_RAINBOW_BEACON, // Full tighter gradient spinning around center of keyboard
RGB_MATRIX_RAINBOW_PINWHEELS, // Full dual gradients spinning two halfs of keyboard
RGB_MATRIX_FLOWER_BLOOMING, // Full tighter gradient of first half scrolling left to right and second half scrolling right to left
RGB_MATRIX_RAINDROPS, // Randomly changes a single key's hue
RGB_MATRIX_JELLYBEAN_RAINDROPS, // Randomly changes a single key's hue and saturation
RGB_MATRIX_HUE_BREATHING, // Hue shifts up a slight ammount at the same time, then shifts back
@ -654,12 +658,12 @@ enum rgb_matrix_effects {
RGB_MATRIX_DIGITAL_RAIN, // That famous computer simulation
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
RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE // Hue & value pulse near multiple key hits then fades value out
RGB_MATRIX_SOLID_REACTIVE_CROSS // Hue & value pulse the same column and row of a single key hit then fades value out
RGB_MATRIX_SOLID_REACTIVE_MULTICROSS // Hue & value pulse the same column and row of multiple key hits then fades value out
RGB_MATRIX_SOLID_REACTIVE_NEXUS // Hue & value pulse away on the same column and row of a single key hit then fades value out
RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS // Hue & value pulse away on the same column and row of multiple key hits then fades value out
RGB_MATRIX_SOLID_REACTIVE_WIDE, // Hue & value pulse near a single key hit then fades value out
RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE, // Hue & value pulse near multiple key hits then fades value out
RGB_MATRIX_SOLID_REACTIVE_CROSS, // Hue & value pulse the same column and row of a single key hit then fades value out
RGB_MATRIX_SOLID_REACTIVE_MULTICROSS, // Hue & value pulse the same column and row of multiple key hits then fades value out
RGB_MATRIX_SOLID_REACTIVE_NEXUS, // Hue & value pulse away on the same column and row of a single key hit then fades value out
RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS, // Hue & value pulse away on the same column and row of multiple key hits then fades value out
RGB_MATRIX_SPLASH, // Full gradient & value pulse away from a single key hit then fades value out
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
@ -694,6 +698,7 @@ You can enable a single effect by defining `ENABLE_[EFFECT_NAME]` in your `confi
|`#define ENABLE_RGB_MATRIX_DUAL_BEACON` |Enables `RGB_MATRIX_DUAL_BEACON` |
|`#define ENABLE_RGB_MATRIX_RAINBOW_BEACON` |Enables `RGB_MATRIX_RAINBOW_BEACON` |
|`#define ENABLE_RGB_MATRIX_RAINBOW_PINWHEELS` |Enables `RGB_MATRIX_RAINBOW_PINWHEELS` |
|`#define ENABLE_RGB_MATRIX_FLOWER_BLOOMING` |Enables `RGB_MATRIX_FLOWER_BLOOMING` |
|`#define ENABLE_RGB_MATRIX_RAINDROPS` |Enables `RGB_MATRIX_RAINDROPS` |
|`#define ENABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS` |Enables `RGB_MATRIX_JELLYBEAN_RAINDROPS` |
|`#define ENABLE_RGB_MATRIX_HUE_BREATHING` |Enables `RGB_MATRIX_HUE_BREATHING` |
@ -875,6 +880,7 @@ These are defined in [`color.h`](https://github.com/qmk/qmk_firmware/blob/master
#define RGB_MATRIX_DEFAULT_MODE RGB_MATRIX_CYCLE_LEFT_RIGHT // Sets the default mode, if none has been set
#define RGB_MATRIX_DEFAULT_HUE 0 // Sets the default hue value, if none has been set
#define RGB_MATRIX_DEFAULT_SAT 255 // Sets the default saturation value, if none has been set
#define RGB_MATRIX_DEFAULT_ON true // Sets the default enabled state, if none has been set
#define RGB_MATRIX_DEFAULT_VAL RGB_MATRIX_MAXIMUM_BRIGHTNESS // Sets the default brightness value, if none has been set
#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)

View File

@ -102,6 +102,7 @@ Your RGB lighting can be configured by placing these `#define`s in your `config.
|`RGBLIGHT_DEFAULT_SAT` |`UINT8_MAX` (255) |The default saturation to use upon clearing the EEPROM |
|`RGBLIGHT_DEFAULT_VAL` |`RGBLIGHT_LIMIT_VAL` |The default value (brightness) to use upon clearing the EEPROM |
|`RGBLIGHT_DEFAULT_SPD` |`0` |The default speed to use upon clearing the EEPROM |
|`RGBLIGHT_DEFAULT_ON` |`true` |Enable RGB lighting upon clearing the EEPROM |
## Effects and Animations
@ -370,9 +371,9 @@ If you need to change your RGB lighting in code, for example in a macro to chang
Example:
```c
sethsv(HSV_WHITE, (LED_TYPE *)&led[0]); // led 0
sethsv(HSV_RED, (LED_TYPE *)&led[1]); // led 1
sethsv(HSV_GREEN, (LED_TYPE *)&led[2]); // led 2
sethsv(HSV_WHITE, (rgb_led_t *)&led[0]); // led 0
sethsv(HSV_RED, (rgb_led_t *)&led[1]); // led 1
sethsv(HSV_GREEN, (rgb_led_t *)&led[2]); // led 2
rgblight_set(); // Utility functions do not call rgblight_set() automatically, so they need to be called explicitly.
```
@ -552,3 +553,33 @@ In addition to setting the Clipping Range, you can use `RGBLIGHT_LED_MAP` togeth
## Hardware Modification
If your keyboard lacks onboard underglow LEDs, you may often be able to solder on an RGB LED strip yourself. You will need to find an unused pin to wire to the data pin of your LED strip. Some keyboards may break out unused pins from the MCU to make soldering easier. The other two pins, VCC and GND, must also be connected to the appropriate power pins.
## Velocikey
Velocikey is a feature that lets you control the speed of lighting effects (like the Rainbow Swirl effect) with the speed of your typing. The faster you type, the faster the lights will go!
### Usage
For Velocikey to take effect, there are two steps. First, when compiling your keyboard, you'll need to set `VELOCIKEY_ENABLE=yes` in `rules.mk`, e.g.:
```
MOUSEKEY_ENABLE = no
STENO_ENABLE = no
EXTRAKEY_ENABLE = yes
VELOCIKEY_ENABLE = yes
```
Then, while using your keyboard, you need to also turn it on with the `VK_TOGG` keycode, which toggles the feature on and off.
The following light effects will all be controlled by Velocikey when it is enabled:
- RGB Breathing
- RGB Rainbow Mood
- RGB Rainbow Swirl
- RGB Snake
- RGB Knight
Support for LED breathing effects is planned but not available yet.
As long as Velocikey is enabled, it will control the speed regardless of any other speed setting that your RGB lights are currently on.
### Configuration
Velocikey doesn't currently support any configuration via keyboard settings. If you want to adjust something like the speed increase or decay rate, you would need to edit `velocikey.c` and adjust the values there to achieve the kinds of speeds that you like.

View File

@ -298,7 +298,7 @@ This enables transmitting the pointing device status to the master side of the s
#define SPLIT_HAPTIC_ENABLE
```
This enables triggering of haptic feedback on the slave side of the split keyboard. For DRV2605L this will send the mode, but for solenoids it is expected that the desired mode is already set up on the slave.
This enables the triggering of haptic feedback on the slave side of the split keyboard. This will send information to the slave side such as the mode, dwell, and whether buzz is enabled.
```c
#define SPLIT_ACTIVITY_ENABLE

View File

@ -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 `UM(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 `UM(BANG)`, `UM(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 `UP(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 `UP()`. 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.

View File

@ -1,29 +0,0 @@
# Velocikey
Velocikey is a feature that lets you control the speed of lighting effects (like the Rainbow Swirl effect) with the speed of your typing. The faster you type, the faster the lights will go!
## Usage
For Velocikey to take effect, there are two steps. First, when compiling your keyboard, you'll need to set `VELOCIKEY_ENABLE=yes` in `rules.mk`, e.g.:
```
MOUSEKEY_ENABLE = no
STENO_ENABLE = no
EXTRAKEY_ENABLE = yes
VELOCIKEY_ENABLE = yes
```
Then, while using your keyboard, you need to also turn it on with the `VK_TOGG` keycode, which toggles the feature on and off.
The following light effects will all be controlled by Velocikey when it is enabled:
- RGB Breathing
- RGB Rainbow Mood
- RGB Rainbow Swirl
- RGB Snake
- RGB Knight
Support for LED breathing effects is planned but not available yet.
As long as Velocikey is enabled, it will control the speed regardless of any other speed setting that your RGB lights are currently on.
## Configuration
Velocikey doesn't currently support any configuration via keyboard settings. If you want to adjust something like the speed increase or decay rate, you would need to edit `velocikey.c` and adjust the values there to achieve the kinds of speeds that you like.

View File

@ -112,7 +112,6 @@
* [分割キーボード](ja/feature_split_keyboard.md)
* [速記](ja/feature_stenography.md)
* [感熱式プリンタ](ja/feature_thermal_printer.md)
* [Velocikey](ja/feature_velocikey.md)
* QMK の開発
* [PR チェックリスト](ja/pr_checklist.md)

View File

@ -1,34 +0,0 @@
# Velocikey
<!---
original document: 0.8.147:docs/feature_velocikey.md
git diff 0.8.147 HEAD -- docs/feature_velocikey.md | cat
-->
Velocikey は入力の速度を使って(レインボー渦巻効果のような)ライト効果の速度を制御できる機能です。速く入力すればするほどライトが速くなります!
## 使用法
Velocikey を使うためには、2つのステップがあります。最初に、キーボードをコンパイルする時に、`rules.mk` に `VELOCIKEY_ENABLE=yes` を設定する必要があります。例えば:
```
MOUSEKEY_ENABLE = no
STENO_ENABLE = no
EXTRAKEY_ENABLE = yes
VELOCIKEY_ENABLE = yes
```
次に、キーボードの使用中に、VLK_TOG キーコードを使って Velocikey を有効にする必要もあります。これは機能をオンおよびオフにします。
以下の全てのライト効果が、Velocikey を有効にすることで制御されます:
- RGB 明滅動作
- RGB レインボームード
- RGB レインボー渦巻
- RGB スネーク
- RGB ナイト
LED 明滅動作の効果のサポートは計画されていますがまだ利用できません。
Velocikey が有効になっている限り、現在オンになっている RGB ライトの他の全ての速度設定に関係なく、速度が制御されます。
## 設定
Velocikey は現在のところキーボード設定を介したどのような設定もサポートしません。速度の増加あるいは減少率などを調整したい場合は、`velocikey.c` を編集し、そこで値を調整して、好みの速度を実現する必要があります。

View File

@ -56,7 +56,7 @@ QMK maintains a Homebrew tap and formula which will automatically install the CL
You will need to install Homebrew. Follow the instructions on https://brew.sh.
!> **NOTE:** If you are using Apple Silicon, such as the M1, you will need to install a rosetta compatible version of Homebrew. This version does not override the base Homebrew. This can be done by running `arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"`. See here: [Rosetta-compatible Homebrew](https://stackoverflow.com/questions/64882584/how-to-run-the-homebrew-installer-under-rosetta-2-on-m1-macbook)
?> If you are using an Apple Silicon machine, the installation process will take significantly longer because GitHub actions do not have native runners to build binary packages for the ARM and AVR toolchains.
#### Installation
@ -64,10 +64,6 @@ Install the QMK CLI by running:
brew install qmk/qmk/qmk
Install the QMK CLI on an Apple Silicon Mac by running:
arch -x86_64 brew install qmk/qmk/qmk
### ** Linux/WSL **
?> **Note for WSL users**: By default, the installation process will clone the QMK repository into your WSL home directory, but if you have cloned manually, ensure that it is located inside the WSL instance instead of the Windows filesystem (ie. not in `/mnt`), as accessing it is currently [extremely slow](https://github.com/microsoft/WSL/issues/4197).

View File

@ -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,6 +39,8 @@ 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
- custom keycode `enum`s must have first entry `= SAFE_RANGE`
@ -79,6 +80,7 @@ https://github.com/qmk/qmk_firmware/pulls?q=is%3Apr+is%3Aclosed+label%3Akeyboard
- Encoder Configuration
- Bootmagic Configuration
- LED Indicator Configuration
- Run `qmk format-json` on this file before submitting your PR. Be sure to append the `-i` flag to directly modify the file, or paste the outputted code into the file.
- `readme.md`
- must follow the [template](https://github.com/qmk/qmk_firmware/blob/master/data/templates/keyboard/readme.md)
- flash command is present, and has `:flash` at end

View File

@ -2,9 +2,13 @@
background-color: #555;
}
.markdown-section p.tip,
.markdown-section tr:nth-child(2n) {
background-color:#444;
background-color:#444;
}
.markdown-section p.tip {
background-color:#555;
color:#FFF;
}
.markdown-section tr {
@ -16,7 +20,7 @@
}
.markdown-section p.tip code {
background-color: #555;
background-color: #333;
color: #fff;
}

View File

@ -857,13 +857,52 @@ void keyboard_post_init_kb(void) {
<!-- tabs:start -->
#### ** Get Geometry **
#### ** Gettters **
These functions allow external code to retrieve the current width, height, rotation, and drawing offsets.
<!-- tabs:start -->
#### ** Width **
```c
uint16_t qp_get_width(painter_device_t device);
```
#### ** Height **
```c
uint16_t qp_get_height(painter_device_t device);
```
#### ** Rotation **
```c
painter_rotation_t qp_get_rotation(painter_device_t device);
```
#### ** Offset X **
```c
uint16_t qp_get_offset_x(painter_device_t device);
```
#### ** Offset Y **
```c
uint16_t qp_get_offset_y(painter_device_t device);
```
##### ** Everything **
Convenience function to call all the previous ones at once.
Note: You can pass `NULL` for the values you are not interested in.
```c
void qp_get_geometry(painter_device_t device, uint16_t *width, uint16_t *height, painter_rotation_t *rotation, uint16_t *offset_x, uint16_t *offset_y);
```
The `qp_get_geometry` function allows external code to retrieve the current width, height, rotation, and drawing offsets.
<!-- tabs:end -->
#### ** Set Viewport Offsets **

View File

@ -53,3 +53,11 @@ The `qp_lvgl_detach` function stops the internal LVGL ticks and releases resourc
## Enabling/Disabling LVGL features :id=lvgl-configuring
You can overwrite LVGL specific features in your `lv_conf.h` file.
## Changing the LVGL task frequency
When LVGL is running, your keyboard's responsiveness may decrease, causing missing keystrokes or encoder rotations, especially during the animation of dynamically-generated content. This occurs because LVGL operates as a scheduled task with a default task rate of five milliseconds. While a fast task rate is advantageous when LVGL is responsible for detecting and processing inputs, it can lead to excessive recalculations of displayed content, which may slow down QMK's matrix scanning. If you rely on QMK instead of LVGL for processing inputs, it can be beneficial to increase the time between calls to the LVGL task handler to better match your preferred display update rate. To do this, add this to your `config.h`:
```c
#define QP_LVGL_TASK_PERIOD 40
```

View File

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

View File

@ -49,6 +49,11 @@ 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()`.
If a complete SPI interface is not required, then the following can be done to disable certain SPI pins, so they don't occupy a GPIO unnecessarily:
- in `config.h`: `#define SPI_MISO_PIN NO_PIN`
- in `config.h`: `#define SPI_MOSI_PIN NO_PIN`
- in `mcuconf.h`: `#define SPI_SELECT_MODE SPI_SELECT_MODE_NONE`, in this case the `slavePin` argument passed to `spi_start()` may be `NO_PIN` if the slave select pin is not used.
## API :id=api
### `void spi_init(void)` :id=api-spi-init

View File

@ -168,6 +168,7 @@ For RGB Matrix, these need to be explicitly enabled as well. To disable any that
#undef ENABLE_RGB_MATRIX_DUAL_BEACON
#undef ENABLE_RGB_MATRIX_RAINBOW_BEACON
#undef ENABLE_RGB_MATRIX_RAINBOW_PINWHEELS
#undef ENABLE_RGB_MATRIX_FLOWER_BLOOMING
#undef ENABLE_RGB_MATRIX_RAINDROPS
#undef ENABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
#undef ENABLE_RGB_MATRIX_HUE_BREATHING

View File

@ -36,7 +36,9 @@ Note how there's several different tests, each mocking out a separate part. Also
## Running the Tests
To run all the tests in the codebase, type `make test:all`. You can also run test matching a substring by typing `make test:matchingsubstring` Note that the tests are always compiled with the native compiler of your platform, so they are also run like any other program on your computer.
To run all the tests in the codebase, type `make test:all`. You can also run test matching a substring by typing `make test:matchingsubstring`. `matchingsubstring` can contain colons to be more specific; `make test:tap_hold_configurations` will run the `tap_hold_configurations` tests for all features while `make test:retro_shift:tap_hold_configurations` will run the `tap_hold_configurations` tests for only the Retro Shift feature.
Note that the tests are always compiled with the native compiler of your platform, so they are also run like any other program on your computer.
## Debugging the Tests

View File

@ -1,189 +1,229 @@
# WS2812 Driver
This driver powers the [RGB Lighting](feature_rgblight.md) and [RGB Matrix](feature_rgb_matrix.md) features.
# WS2812 Driver :id=ws2812-driver
Currently QMK supports the following addressable LEDs (however, the white LED in RGBW variants is not supported):
This driver provides support for WorldSemi addressable RGB(W) LEDs, and compatible equivalents:
WS2811, WS2812, WS2812B, WS2812C, etc.
SK6812, SK6812MINI, SK6805
* WS2811, WS2812, WS2812B, WS2812C, etc.
* SK6812, SK6812MINI, SK6805
These LEDs are called "addressable" because instead of using a wire per color, each LED contains a small microchip that understands a special protocol sent over a single wire. The chip passes on the remaining data to the next LED, allowing them to be chained together. In this way, you can easily control the color of the individual LEDs.
These LEDs are often called "addressable" because instead of using a wire per color (and per LED), each LED contains a small microchip that understands a special protocol sent over a single wire.
The LEDs can be chained together, and the remaining data is passed on to the next. In this way, you can easily control the color of many LEDs using a single GPIO.
## Supported Driver Types
## Usage :id=usage
| | AVR | ARM |
| -------- | ------------------ | ------------------ |
| bit bang | :heavy_check_mark: | :heavy_check_mark: |
| I2C | :heavy_check_mark: | |
| SPI | | :heavy_check_mark: |
| PWM | | :heavy_check_mark: |
| PIO | | :heavy_check_mark: |
In most cases, the WS2812 driver code is automatically included if you are using either the [RGBLight](feature_rgblight.md) or [RGB Matrix](feature_rgb_matrix.md) feature with the `ws2812` driver set, and you would use those APIs instead.
## Driver configuration
However, if you need to use the driver standalone, add the following to your `rules.mk`:
### All drivers
Different versions of the addressable LEDs have differing requirements for the T<sub>RST</sub> period between frames.
The default setting is 280 µs, which should work for most cases, but this can be overridden in your config.h. e.g.:
```c
#define WS2812_TRST_US 80
```make
WS2812_DRIVER_REQUIRED = yes
```
#### Byte Order
You can then call the WS2812 API by including `ws2812.h` in your code.
Some variants of the WS2812 may have their color components in a different physical or logical order. For example, the WS2812B-2020 has physically swapped red and green LEDs, which causes the wrong color to be displayed, because the default order of the bytes sent over the wire is defined as GRB.
In this case, you can change the byte order by defining `WS2812_BYTE_ORDER` as one of the following values:
## Basic Configuration :id=basic-configuration
| Byte order | Known devices |
| --------------------------------- | ----------------------------- |
| `WS2812_BYTE_ORDER_GRB` (default) | Most WS2812's, SK6812, SK6805 |
| `WS2812_BYTE_ORDER_RGB` | WS2812B-2020 |
| `WS2812_BYTE_ORDER_BGR` | TM1812 |
Add the following to your `config.h`:
|Define |Default |Description |
|-------------------|-----------------------|------------------------------------------------------------------------------------------------|
|`WS2812_DI_PIN` |*Not defined* |The GPIO pin connected to the DI pin of the first LED in the chain |
|`WS2812_LED_COUNT` |*Not defined* |Number of LEDs in the WS2812 chain - automatically set when RGBLight or RGB Matrix is configured|
|`WS2812_TIMING` |`1250` |The total length of a bit (TH+TL) in nanoseconds |
|`WS2812_T1H` |`900` |The length of a "1" bit's high phase in nanoseconds |
|`WS2812_T0H` |`350` |The length of a "0" bit's high phase in nanoseconds |
|`WS2812_TRST_US` |`280` |The length of the reset phase in microseconds |
|`WS2812_BYTE_ORDER`|`WS2812_BYTE_ORDER_GRB`|The byte order of the RGB data |
### Bitbang
Default driver, the absence of configuration assumes this driver. To configure it, add this to your rules.mk:
### Timing Adjustment :id=timing-adjustment
The WS2812 LED communication protocol works by encoding a "1" bit with a long high pulse (T<sub>1</sub>H), and a "0" bit with a shorter pulse (T<sub>0</sub>H). The total cycle length of a bit is the same.
The "reset" pulse (T<sub>RST</sub>) latches the sent RGB data to all of the LEDs and denotes a completed "frame".
Some WS2812 variants have slightly different timing parameter requirements, which can be accounted for if necessary using the above `#define`s in your `config.h`.
### Byte Order :id=byte-order
Some WS2812 variants may have their color components in a different physical or logical order. For example, the WS2812B-2020 has physically swapped red and green LEDs, which causes the wrong color to be displayed, because the default order of the bytes sent over the wire is defined as GRB.
If you find your LED colors are consistently swapped, you may need to change the byte order by adding the following to your `config.h`:
```c
#define WS2812_BYTE_ORDER WS2812_BYTE_ORDER_GRB
```
Where the byte order may be one of:
|Byte Order|Known Devices |
|----------|----------------------------|
|`GRB` |Most WS2812s, SK6812, SK6805|
|`RGB` |WS2812B-2020 |
|`BGR` |TM1812 |
## Driver Configuration :id=driver-configuration
Driver selection can be configured in `rules.mk` as `WS2812_DRIVER`, or in `info.json` as `ws2812.driver`. Valid values are `bitbang` (default), `i2c`, `spi`, `pwm`, `vendor`, or `custom`. See below for information on individual drivers.
### Bitbang Driver :id=bitbang-driver
This is the default WS2812 driver. It operates by "bit-banging" ie. directly toggling the GPIO.
Please note that on AVR devices, due to the tight timing requirements longer chains and/or heavy CPU loads may cause visible lag. Unfortunately this driver is usually the only option for AVR.
```make
WS2812_DRIVER = bitbang
```
!> This driver is not hardware accelerated and may not be performant on heavily loaded systems.
### I2C Driver :id=i2c-driver
#### Adjusting bit timings
The WS2812 LED communication topology depends on a serialized timed window. Different versions of the addressable LEDs have differing requirements for the timing parameters, for instance, of the SK6812.
You can tune these parameters through the definition of the following macros:
| Macro | Default | AVR | ARM |
| --------------- | ---------------------------- | ------------------ | ------------------ |
| `WS2812_TIMING` | `1250` | :heavy_check_mark: | :heavy_check_mark: |
| `WS2812_T0H` | `350` | :heavy_check_mark: | :heavy_check_mark: |
| `WS2812_T0L` | `WS2812_TIMING - WS2812_T0H` | | :heavy_check_mark: |
| `WS2812_T1H` | `900` | :heavy_check_mark: | :heavy_check_mark: |
| `WS2812_T1L` | `WS2812_TIMING - WS2812_T1H` | | :heavy_check_mark: |
### I2C
Targeting boards where WS2812 support is offloaded to a 2nd MCU. Currently the driver is limited to AVR given the known consumers are ps2avrGB/BMC. To configure it, add this to your rules.mk:
A specialized driver mainly used for PS2AVRGB (Bootmapper Client) boards, which possess an ATtiny85 that handles the WS2812 LEDs.
```make
WS2812_DRIVER = i2c
```
Configure the hardware via your config.h:
```c
#define WS2812_I2C_ADDRESS 0xB0 // default: 0xB0
#define WS2812_I2C_TIMEOUT 100 // default: 100
```
The following `#define`s apply only to the `i2c` driver:
### SPI
Targeting STM32 boards where WS2812 support is offloaded to an SPI hardware device. The advantage is that the use of DMA offloads processing of the WS2812 protocol from the MCU. `WS2812_DI_PIN` for this driver is the configured SPI MOSI pin. Due to the nature of repurposing SPI to drive the LEDs, the other SPI pins, MISO and SCK, **must** remain unused. To configure it, add this to your rules.mk:
|Define |Default|Description |
|--------------------|-------|---------------------------------|
|`WS2812_I2C_ADDRESS`|`0xB0` |The I2C address of the ATtiny85. |
|`WS2812_I2C_TIMEOUT`|`100` |The I2C timeout, in milliseconds.|
```make
WS2812_DRIVER = spi
```
### PIO Driver :id=pio-driver
Configure the hardware via your config.h:
```c
#define WS2812_SPI SPID1 // default: SPID1
#define WS2812_SPI_MOSI_PAL_MODE 5 // MOSI pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 5
#define WS2812_SPI_SCK_PIN B3 // Required for F072, may be for others -- SCK pin, see the respective datasheet for the appropriate values for your MCU. default: unspecified
#define WS2812_SPI_SCK_PAL_MODE 5 // SCK pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 5
```
This driver is RP2040-only, and leverages the onboard PIO (programmable I/O) system and DMA to offload processing from the CPU.
You must also turn on the SPI feature in your halconf.h and mcuconf.h
#### Circular Buffer Mode
Some boards may flicker while in the normal buffer mode. To fix this issue, circular buffer mode may be used to rectify the issue.
By default, the circular buffer mode is disabled.
To enable this alternative buffer mode, place this into your `config.h` file:
```c
#define WS2812_SPI_USE_CIRCULAR_BUFFER
```
#### Setting baudrate with divisor
To adjust the baudrate at which the SPI peripheral is configured, users will need to derive the target baudrate from the clock tree provided by STM32CubeMX.
Only divisors of 2, 4, 8, 16, 32, 64, 128 and 256 are supported by hardware.
| Define | Default | Description |
| -------------------- | ------- | ----------------------------------- |
| `WS2812_SPI_DIVISOR` | `16` | SPI source clock peripheral divisor |
#### Testing Notes
While not an exhaustive list, the following table provides the scenarios that have been partially validated:
| | SPI1 | SPI2 | SPI3 |
| ---- | ------------------------------------------- | --------------------------------------- | --------------------- |
| f072 | ? | B15 :heavy_check_mark: (needs SCK: B13) | N/A |
| f103 | A7 :heavy_check_mark: | B15 :heavy_check_mark: | N/A |
| f303 | A7 :heavy_check_mark: B5 :heavy_check_mark: | B15 :heavy_check_mark: | B5 :heavy_check_mark: |
*Other supported ChibiOS boards and/or pins may function, it will be highly chip and configuration dependent.*
### PWM
Targeting STM32 boards where WS2812 support is offloaded to an PWM timer and DMA stream. The advantage is that the use of DMA offloads processing of the WS2812 protocol from the MCU. To configure it, add this to your rules.mk:
```make
WS2812_DRIVER = pwm
```
Configure the hardware via your config.h:
```c
#define WS2812_PWM_DRIVER PWMD2 // default: PWMD2
#define WS2812_PWM_CHANNEL 2 // default: 2
#define WS2812_PWM_PAL_MODE 2 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 2
//#define WS2812_PWM_COMPLEMENTARY_OUTPUT // Define for a complementary timer output (TIMx_CHyN); omit for a normal timer output (TIMx_CHy).
#define WS2812_DMA_STREAM STM32_DMA1_STREAM2 // DMA Stream for TIMx_UP, see the respective reference manual for the appropriate values for your MCU.
#define WS2812_DMA_CHANNEL 2 // DMA Channel for TIMx_UP, see the respective reference manual for the appropriate values for your MCU.
#define WS2812_DMAMUX_ID STM32_DMAMUX1_TIM2_UP // DMAMUX configuration for TIMx_UP -- only required if your MCU has a DMAMUX peripheral, see the respective reference manual for the appropriate values for your MCU.
```
Note that using a complementary timer output (TIMx_CHyN) is possible only for advanced-control timers (TIM1, TIM8, TIM20 on STM32), and the `STM32_PWM_USE_ADVANCED` option in mcuconf.h must be set to `TRUE`. Complementary outputs of general-purpose timers are not supported due to ChibiOS limitations.
You must also turn on the PWM feature in your halconf.h and mcuconf.h
#### Testing Notes
While not an exhaustive list, the following table provides the scenarios that have been partially validated:
| | Status |
| --------- | ------------------ |
| f072 | ? |
| f103 | :heavy_check_mark: |
| f303 | :heavy_check_mark: |
| f401/f411 | :heavy_check_mark: |
*Other supported ChibiOS boards and/or pins may function, it will be highly chip and configuration dependent.*
### PIO
Targeting Raspberry Pi RP2040 boards only where WS2812 support is offloaded to an dedicated PIO implementation. This offloads processing of the WS2812 protocol from the MCU to a dedicated PIO program using DMA transfers.
To configure it, add this to your rules.mk:
The WS2812 PIO program uses one state machine, six instructions and one DMA interrupt handler callback. Due to the implementation the time resolution for this driver is 50 ns - any value not specified in this interval will be rounded to the next matching interval.
```make
WS2812_DRIVER = vendor
```
Configure the hardware via your config.h:
```c
#define WS2812_PIO_USE_PIO1 // Force the usage of PIO1 peripheral, by default the WS2812 implementation uses the PIO0 peripheral
### PWM Driver :id=pwm-driver
This driver is ARM-only, and leverages the onboard PWM peripheral and DMA to offload processing from the CPU.
```make
WS2812_DRIVER = pwm
```
The WS2812 PIO programm uses 1 state machine, 6 instructions and one DMA interrupt handler callback. Due to the implementation the time resolution for this drivers is 50ns, any value not specified in this interval will be rounded to the next matching interval.
### SPI Driver :id=spi-driver
### Push Pull and Open Drain Configuration
The default configuration is a push pull on the defined pin.
This can be configured for bitbang, PWM and SPI.
This driver is ARM-only, and leverages the onboard SPI peripheral and DMA to offload processing from the CPU. The DI pin **must** be connected to the MOSI pin on the MCU, and all other SPI pins **must** be left unused. This is also very dependent on your MCU's SPI peripheral clock speed, and may or may not be possible depending on the MCU selected.
Note: This only applies to STM32 boards.
```make
WS2812_DRIVER = spi
```
## ChibiOS/ARM Configuration :id=arm-configuration
The following defines apply only to ARM devices:
|Define |Default |Description |
|------------|------------------------------|---------------------------------------------------------------------------------|
|`WS2812_T1L`|`(WS2812_TIMING - WS2812_T1H)`|The length of a "1" bit's low phase in nanoseconds (bitbang and PIO drivers only)|
|`WS2812_T0L`|`(WS2812_TIMING - WS2812_T0H)`|The length of a "0" bit's low phase in nanoseconds (bitbang and PIO drivers only)|
### Push-Pull and Open Drain :id=push-pull-open-drain
By default, the GPIO used for data transmission is configured as a *push-pull* output, meaning the pin is effectively always driven either to VCC or to ground.
For situations where the logic level voltage is lower than the power supply voltage, however, this can pose an issue. The solution is to configure the pin for *open drain* mode instead, and use a pullup resistor between the DI pin and VCC. In this mode, the MCU can only pull the GPIO *low*, or leave it floating. The pullup resistor is then responsible for pulling the line high, when the MCU is not driving the GPIO.
To configure the DI pin for open drain configuration, add the following to your `config.h`:
To configure the `WS2812_DI_PIN` to open drain configuration add this to your config.h file:
```c
#define WS2812_EXTERNAL_PULLUP
```
### SPI Driver :id=arm-spi-driver
Depending on the ChibiOS board configuration, you may need to enable SPI at the keyboard level. For STM32, this would look like:
`halconf.h`:
```c
#define HAL_USE_SPI TRUE
```
`mcuconf.h`:
```c
#undef STM32_SPI_USE_SPI1
#define STM32_SPI_USE_SPI1 TRUE
```
The following `define`s apply only to the `spi` driver:
|Define |Default |Description |
|--------------------------------|-------------|-------------------------------------------------------------------------------|
|`WS2812_SPI_DRIVER` |`SPID1` |The SPI driver to use |
|`WS2812_SPI_MOSI_PAL_MODE` |`5` |The MOSI pin alternative function to use |
|`WS2812_SPI_SCK_PIN` |*Not defined*|The SCK pin - required for F072 and possibly others |
|`WS2812_SPI_SCK_PAL_MODE` |`5` |The SCK pin alternative function to use - required for F072 and possibly others|
|`WS2812_SPI_DIVISOR` |`16` |The divisor used to adjust the baudrate |
|`WS2812_SPI_USE_CIRCULAR_BUFFER`|*Not defined*|Enable a circular buffer for improved rendering |
#### Setting the Baudrate :id=arm-spi-baudrate
To adjust the SPI baudrate, you will need to derive the target baudrate from the clock tree provided by STM32CubeMX, and add the following to your `config.h`:
```c
#define WS2812_SPI_DIVISOR 16
```
Only divisors of 2, 4, 8, 16, 32, 64, 128 and 256 are supported on STM32 devices. Other MCUs may have similar constraints -- check the reference manual for your respective MCU for specifics.
#### Circular Buffer :id=arm-spi-circular-buffer
A circular buffer can be enabled if you experience flickering.
To enable the circular buffer, add the following to your `config.h`:
```c
#define WS2812_SPI_USE_CIRCULAR_BUFFER
```
### PIO Driver :id=arm-pio-driver
The following `#define`s apply only to the PIO driver:
|Define |Default |Description |
|---------------------|-------------|---------------------------------------|
|`WS2812_PIO_USE_PIO1`|*Not defined*|Use the PIO1 peripheral instead of PIO0|
### 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_TIM2
#define STM32_PWM_USE_TIM2 TRUE
```
The following `#define`s apply only to the `pwm` driver:
|Define |Default |Description |
|---------------------------------|--------------------|------------------------------------------------------------------------------------------|
|`WS2812_PWM_DRIVER` |`PWMD2` |The PWM driver to use |
|`WS2812_PWM_CHANNEL` |`2` |The PWM channel to use |
|`WS2812_PWM_PAL_MODE` |`2` |The pin alternative function to use |
|`WS2812_DMA_STREAM` |`STM32_DMA1_STREAM2`|The DMA Stream for `TIMx_UP` |
|`WS2812_DMA_CHANNEL` |`2` |The DMA Channel for `TIMx_UP` |
|`WS2812_DMAMUX_ID` |*Not defined* |The DMAMUX configuration for `TIMx_UP` - only required if your MCU has a DMAMUX peripheral|
|`WS2812_PWM_COMPLEMENTARY_OUTPUT`|*Not defined* |Whether the PWM output is complementary (`TIMx_CHyN`) |
?> Using a complementary timer output (`TIMx_CHyN`) is possible only for advanced-control timers (1, 8 and 20 on STM32), and the `STM32_PWM_USE_ADVANCED` option in `mcuconf.h` must be set to `TRUE`. Complementary outputs of general-purpose timers are not supported due to ChibiOS limitations.
## API :id=api
### `void ws2812_setleds(rgb_led_t *ledarray, uint16_t number_of_leds)` :id=api-ws2812-setleds
Send RGB data to the WS2812 LED chain.
#### Arguments :id=api-ws2812-setleds-arguments
- `rgb_led_t *ledarray`
A pointer to the LED array.
- `uint16_t number_of_leds`
The length of the LED array.

View File

@ -121,7 +121,6 @@
* [分体式键盘](zh-cn/feature_split_keyboard.md)
* [速记](zh-cn/feature_stenography.md)
* [热敏打印机](zh-cn/feature_thermal_printer.md)
* [Velocikey](zh-cn/feature_velocikey.md)
* QMK开发
* [PR Checklist](zh-cn/pr_checklist.md)

View File

@ -23,7 +23,6 @@
#include "util.h"
#include <stdlib.h>
uint8_t solenoid_dwell = SOLENOID_DEFAULT_DWELL;
static pin_t solenoid_pads[] = SOLENOID_PINS;
#define NUMBER_OF_SOLENOIDS ARRAY_SIZE(solenoid_pads)
bool solenoid_on[NUMBER_OF_SOLENOIDS] = {false};
@ -53,7 +52,7 @@ void solenoid_set_buzz(uint8_t buzz) {
}
void solenoid_set_dwell(uint8_t dwell) {
solenoid_dwell = dwell;
haptic_set_dwell(dwell);
}
/**
@ -119,7 +118,7 @@ void solenoid_check(void) {
elapsed[i] = timer_elapsed(solenoid_start[i]);
// Check if it's time to finish this solenoid click cycle
if (elapsed[i] > solenoid_dwell) {
if (elapsed[i] > haptic_config.dwell) {
solenoid_stop(i);
continue;
}

View File

@ -24,7 +24,7 @@
# elif defined(PROTOCOL_CHIBIOS)
# include "hal.h"
# include "chibios_config.h"
# if defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F3XX) || defined(STM32F4XX) || defined(STM32L0XX) || defined(GD32VF103)
# if defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F3XX) || defined(STM32F4XX) || defined(STM32L0XX) || defined(GD32VF103) || defined(MCU_RP)
# define APA102_NOPS (100 / (1000000000L / (CPU_CLOCK / 4))) // This calculates how many loops of 4 nops to run to delay 100 ns
# else
# error APA102_NOPS configuration required
@ -61,18 +61,18 @@ void static apa102_end_frame(uint16_t num_leds);
void static apa102_send_frame(uint8_t red, uint8_t green, uint8_t blue, uint8_t brightness);
void static apa102_send_byte(uint8_t byte);
void apa102_setleds(LED_TYPE *start_led, uint16_t num_leds) {
LED_TYPE *end = start_led + num_leds;
void apa102_setleds(rgb_led_t *start_led, uint16_t num_leds) {
rgb_led_t *end = start_led + num_leds;
apa102_start_frame();
for (LED_TYPE *led = start_led; led < end; led++) {
for (rgb_led_t *led = start_led; led < end; led++) {
apa102_send_frame(led->r, led->g, led->b, apa102_led_brightness);
}
apa102_end_frame(num_leds);
}
// Overwrite the default rgblight_call_driver to use apa102 driver
void rgblight_call_driver(LED_TYPE *start_led, uint8_t num_leds) {
void rgblight_call_driver(rgb_led_t *start_led, uint8_t num_leds) {
apa102_setleds(start_led, num_leds);
}

View File

@ -37,5 +37,5 @@ extern uint8_t apa102_led_brightness;
* - Set the data-out pin as output
* - Send out the LED data
*/
void apa102_setleds(LED_TYPE *start_led, uint16_t num_leds);
void apa102_setleds(rgb_led_t *start_led, uint16_t num_leds);
void apa102_set_brightness(uint8_t brightness);

View File

@ -1,168 +0,0 @@
/* Copyright 2021 Jasper Chan
* 2023 Huckies <https://github.com/Huckies>
*
* 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 "aw20216.h"
#include "wait.h"
#include "spi_master.h"
/* The AW20216 appears to be somewhat similar to the IS31FL743, although quite
* a few things are different, such as the command byte format and page ordering.
* The LED addresses start from 0x00 instead of 0x01.
*/
#define AWINIC_ID 0b1010 << 4
#define AW_PAGE_FUNCTION 0x00 << 1 // PG0, Function registers
#define AW_PAGE_PWM 0x01 << 1 // PG1, LED PWM control
#define AW_PAGE_SCALING 0x02 << 1 // PG2, LED current scaling control
#define AW_PAGE_PATCHOICE 0x03 << 1 // PG3, Pattern choice?
#define AW_PAGE_PWMSCALING 0x04 << 1 // PG4, LED PWM + Scaling control?
#define AW_WRITE 0
#define AW_READ 1
#define AW_REG_CONFIGURATION 0x00 // PG0
#define AW_REG_GLOBALCURRENT 0x01 // PG0
#define AW_REG_RESET 0x2F // PG0
#define AW_REG_MIXFUNCTION 0x46 // PG0
// Default value of AW_REG_CONFIGURATION
// D7:D4 = 1011, SWSEL (SW1~SW12 active)
// D3 = 0?, reserved (apparently this should be 1 but it doesn't seem to matter)
// D2:D1 = 00, OSDE (open/short detection enable)
// D0 = 0, CHIPEN (write 1 to enable LEDs when hardware enable pulled high)
#define AW_CONFIG_DEFAULT 0b10110000
#define AW_MIXCR_DEFAULT 0b00000000
#define AW_RESET_CMD 0xAE
#define AW_CHIPEN 1
#define AW_LPEN (0x01 << 1)
#define AW_PWM_REGISTER_COUNT 216
#ifndef AW_SCALING_MAX
# define AW_SCALING_MAX 150
#endif
#ifndef AW_GLOBAL_CURRENT_MAX
# define AW_GLOBAL_CURRENT_MAX 150
#endif
#ifndef AW_SPI_MODE
# define AW_SPI_MODE 0
#endif
#ifndef AW_SPI_DIVISOR
# define AW_SPI_DIVISOR 4
#endif
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) {
static uint8_t s_spi_transfer_buffer[2] = {0};
if (!spi_start(cs_pin, false, AW_SPI_MODE, AW_SPI_DIVISOR)) {
spi_stop();
return false;
}
s_spi_transfer_buffer[0] = (AWINIC_ID | page | AW_WRITE);
s_spi_transfer_buffer[1] = reg;
if (spi_transmit(s_spi_transfer_buffer, 2) != SPI_STATUS_SUCCESS) {
spi_stop();
return false;
}
if (spi_transmit(data, len) != SPI_STATUS_SUCCESS) {
spi_stop();
return false;
}
spi_stop();
return true;
}
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);
}
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) {
// 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);
}
}
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);
}
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);
}
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) {
setPinOutput(en_pin);
writePinHigh(en_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_soft_enable(cs_pin);
aw20216_auto_lowpower(cs_pin);
}
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) {
for (uint8_t i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
aw20216_set_color(i, red, green, blue);
}
}
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);
}
g_pwm_buffer_update_required[index] = false;
}

View File

@ -1,253 +0,0 @@
/* Copyright 2021 Jasper Chan (Gigahawk)
*
* 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>
#include <stdbool.h>
#include "progmem.h"
#include "gpio.h"
typedef struct aw_led {
uint8_t driver : 2;
uint8_t r;
uint8_t g;
uint8_t b;
} 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);
#define CS1_SW1 0x00
#define CS2_SW1 0x01
#define CS3_SW1 0x02
#define CS4_SW1 0x03
#define CS5_SW1 0x04
#define CS6_SW1 0x05
#define CS7_SW1 0x06
#define CS8_SW1 0x07
#define CS9_SW1 0x08
#define CS10_SW1 0x09
#define CS11_SW1 0x0A
#define CS12_SW1 0x0B
#define CS13_SW1 0x0C
#define CS14_SW1 0x0D
#define CS15_SW1 0x0E
#define CS16_SW1 0x0F
#define CS17_SW1 0x10
#define CS18_SW1 0x11
#define CS1_SW2 0x12
#define CS2_SW2 0x13
#define CS3_SW2 0x14
#define CS4_SW2 0x15
#define CS5_SW2 0x16
#define CS6_SW2 0x17
#define CS7_SW2 0x18
#define CS8_SW2 0x19
#define CS9_SW2 0x1A
#define CS10_SW2 0x1B
#define CS11_SW2 0x1C
#define CS12_SW2 0x1D
#define CS13_SW2 0x1E
#define CS14_SW2 0x1F
#define CS15_SW2 0x20
#define CS16_SW2 0x21
#define CS17_SW2 0x22
#define CS18_SW2 0x23
#define CS1_SW3 0x24
#define CS2_SW3 0x25
#define CS3_SW3 0x26
#define CS4_SW3 0x27
#define CS5_SW3 0x28
#define CS6_SW3 0x29
#define CS7_SW3 0x2A
#define CS8_SW3 0x2B
#define CS9_SW3 0x2C
#define CS10_SW3 0x2D
#define CS11_SW3 0x2E
#define CS12_SW3 0x2F
#define CS13_SW3 0x30
#define CS14_SW3 0x31
#define CS15_SW3 0x32
#define CS16_SW3 0x33
#define CS17_SW3 0x34
#define CS18_SW3 0x35
#define CS1_SW4 0x36
#define CS2_SW4 0x37
#define CS3_SW4 0x38
#define CS4_SW4 0x39
#define CS5_SW4 0x3A
#define CS6_SW4 0x3B
#define CS7_SW4 0x3C
#define CS8_SW4 0x3D
#define CS9_SW4 0x3E
#define CS10_SW4 0x3F
#define CS11_SW4 0x40
#define CS12_SW4 0x41
#define CS13_SW4 0x42
#define CS14_SW4 0x43
#define CS15_SW4 0x44
#define CS16_SW4 0x45
#define CS17_SW4 0x46
#define CS18_SW4 0x47
#define CS1_SW5 0x48
#define CS2_SW5 0x49
#define CS3_SW5 0x4A
#define CS4_SW5 0x4B
#define CS5_SW5 0x4C
#define CS6_SW5 0x4D
#define CS7_SW5 0x4E
#define CS8_SW5 0x4F
#define CS9_SW5 0x50
#define CS10_SW5 0x51
#define CS11_SW5 0x52
#define CS12_SW5 0x53
#define CS13_SW5 0x54
#define CS14_SW5 0x55
#define CS15_SW5 0x56
#define CS16_SW5 0x57
#define CS17_SW5 0x58
#define CS18_SW5 0x59
#define CS1_SW6 0x5A
#define CS2_SW6 0x5B
#define CS3_SW6 0x5C
#define CS4_SW6 0x5D
#define CS5_SW6 0x5E
#define CS6_SW6 0x5F
#define CS7_SW6 0x60
#define CS8_SW6 0x61
#define CS9_SW6 0x62
#define CS10_SW6 0x63
#define CS11_SW6 0x64
#define CS12_SW6 0x65
#define CS13_SW6 0x66
#define CS14_SW6 0x67
#define CS15_SW6 0x68
#define CS16_SW6 0x69
#define CS17_SW6 0x6A
#define CS18_SW6 0x6B
#define CS1_SW7 0x6C
#define CS2_SW7 0x6D
#define CS3_SW7 0x6E
#define CS4_SW7 0x6F
#define CS5_SW7 0x70
#define CS6_SW7 0x71
#define CS7_SW7 0x72
#define CS8_SW7 0x73
#define CS9_SW7 0x74
#define CS10_SW7 0x75
#define CS11_SW7 0x76
#define CS12_SW7 0x77
#define CS13_SW7 0x78
#define CS14_SW7 0x79
#define CS15_SW7 0x7A
#define CS16_SW7 0x7B
#define CS17_SW7 0x7C
#define CS18_SW7 0x7D
#define CS1_SW8 0x7E
#define CS2_SW8 0x7F
#define CS3_SW8 0x80
#define CS4_SW8 0x81
#define CS5_SW8 0x82
#define CS6_SW8 0x83
#define CS7_SW8 0x84
#define CS8_SW8 0x85
#define CS9_SW8 0x86
#define CS10_SW8 0x87
#define CS11_SW8 0x88
#define CS12_SW8 0x89
#define CS13_SW8 0x8A
#define CS14_SW8 0x8B
#define CS15_SW8 0x8C
#define CS16_SW8 0x8D
#define CS17_SW8 0x8E
#define CS18_SW8 0x8F
#define CS1_SW9 0x90
#define CS2_SW9 0x91
#define CS3_SW9 0x92
#define CS4_SW9 0x93
#define CS5_SW9 0x94
#define CS6_SW9 0x95
#define CS7_SW9 0x96
#define CS8_SW9 0x97
#define CS9_SW9 0x98
#define CS10_SW9 0x99
#define CS11_SW9 0x9A
#define CS12_SW9 0x9B
#define CS13_SW9 0x9C
#define CS14_SW9 0x9D
#define CS15_SW9 0x9E
#define CS16_SW9 0x9F
#define CS17_SW9 0xA0
#define CS18_SW9 0xA1
#define CS1_SW10 0xA2
#define CS2_SW10 0xA3
#define CS3_SW10 0xA4
#define CS4_SW10 0xA5
#define CS5_SW10 0xA6
#define CS6_SW10 0xA7
#define CS7_SW10 0xA8
#define CS8_SW10 0xA9
#define CS9_SW10 0xAA
#define CS10_SW10 0xAB
#define CS11_SW10 0xAC
#define CS12_SW10 0xAD
#define CS13_SW10 0xAE
#define CS14_SW10 0xAF
#define CS15_SW10 0xB0
#define CS16_SW10 0xB1
#define CS17_SW10 0xB2
#define CS18_SW10 0xB3
#define CS1_SW11 0xB4
#define CS2_SW11 0xB5
#define CS3_SW11 0xB6
#define CS4_SW11 0xB7
#define CS5_SW11 0xB8
#define CS6_SW11 0xB9
#define CS7_SW11 0xBA
#define CS8_SW11 0xBB
#define CS9_SW11 0xBC
#define CS10_SW11 0xBD
#define CS11_SW11 0xBE
#define CS12_SW11 0xBF
#define CS13_SW11 0xC0
#define CS14_SW11 0xC1
#define CS15_SW11 0xC2
#define CS16_SW11 0xC3
#define CS17_SW11 0xC4
#define CS18_SW11 0xC5
#define CS1_SW12 0xC6
#define CS2_SW12 0xC7
#define CS3_SW12 0xC8
#define CS4_SW12 0xC9
#define CS5_SW12 0xCA
#define CS6_SW12 0xCB
#define CS7_SW12 0xCC
#define CS8_SW12 0xCD
#define CS9_SW12 0xCE
#define CS10_SW12 0xCF
#define CS11_SW12 0xD0
#define CS12_SW12 0xD1
#define CS13_SW12 0xD2
#define CS14_SW12 0xD3
#define CS15_SW12 0xD4
#define CS16_SW12 0xD5
#define CS17_SW12 0xD6
#define CS18_SW12 0xD7

168
drivers/led/aw20216s.c Normal file
View File

@ -0,0 +1,168 @@
/* Copyright 2021 Jasper Chan
* 2023 Huckies <https://github.com/Huckies>
*
* 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 "aw20216s.h"
#include "wait.h"
#include "spi_master.h"
/* The AW20216S appears to be somewhat similar to the IS31FL743, although quite
* a few things are different, such as the command byte format and page ordering.
* The LED addresses start from 0x00 instead of 0x01.
*/
#define AW20216S_ID 0b1010 << 4
#define AW20216S_PAGE_FUNCTION 0x00 << 1 // PG0, Function registers
#define AW20216S_PAGE_PWM 0x01 << 1 // PG1, LED PWM control
#define AW20216S_PAGE_SCALING 0x02 << 1 // PG2, LED current scaling control
#define AW20216S_PAGE_PATCHOICE 0x03 << 1 // PG3, Pattern choice?
#define AW20216S_PAGE_PWMSCALING 0x04 << 1 // PG4, LED PWM + Scaling control?
#define AW20216S_WRITE 0
#define AW20216S_READ 1
#define AW20216S_REG_CONFIGURATION 0x00 // PG0
#define AW20216S_REG_GLOBALCURRENT 0x01 // PG0
#define AW20216S_REG_RESET 0x2F // PG0
#define AW20216S_REG_MIXFUNCTION 0x46 // PG0
// Default value of AW20216S_REG_CONFIGURATION
// D7:D4 = 1011, SWSEL (SW1~SW12 active)
// D3 = 0?, reserved (apparently this should be 1 but it doesn't seem to matter)
// D2:D1 = 00, OSDE (open/short detection enable)
// D0 = 0, CHIPEN (write 1 to enable LEDs when hardware enable pulled high)
#define AW20216S_CONFIG_DEFAULT 0b10110000
#define AW20216S_MIXCR_DEFAULT 0b00000000
#define AW20216S_RESET_CMD 0xAE
#define AW20216S_CHIPEN 1
#define AW20216S_LPEN (0x01 << 1)
#define AW20216S_PWM_REGISTER_COUNT 216
#ifndef AW20216S_SCALING_MAX
# define AW20216S_SCALING_MAX 150
#endif
#ifndef AW20216S_GLOBAL_CURRENT_MAX
# define AW20216S_GLOBAL_CURRENT_MAX 150
#endif
#ifndef AW20216S_SPI_MODE
# define AW20216S_SPI_MODE 0
#endif
#ifndef AW20216S_SPI_DIVISOR
# define AW20216S_SPI_DIVISOR 4
#endif
uint8_t g_pwm_buffer[AW20216S_DRIVER_COUNT][AW20216S_PWM_REGISTER_COUNT];
bool g_pwm_buffer_update_required[AW20216S_DRIVER_COUNT] = {false};
bool aw20216s_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, AW20216S_SPI_MODE, AW20216S_SPI_DIVISOR)) {
spi_stop();
return false;
}
s_spi_transfer_buffer[0] = (AW20216S_ID | page | AW20216S_WRITE);
s_spi_transfer_buffer[1] = reg;
if (spi_transmit(s_spi_transfer_buffer, 2) != SPI_STATUS_SUCCESS) {
spi_stop();
return false;
}
if (spi_transmit(data, len) != SPI_STATUS_SUCCESS) {
spi_stop();
return false;
}
spi_stop();
return true;
}
static inline bool aw20216s_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 aw20216s_write(cs_pin, page, reg, &value, 1);
}
void aw20216s_soft_reset(pin_t cs_pin) {
aw20216s_write_register(cs_pin, AW20216S_PAGE_FUNCTION, AW20216S_REG_RESET, AW20216S_RESET_CMD);
}
static void aw20216s_init_scaling(pin_t cs_pin) {
// Set constant current to the max, control brightness with PWM
for (uint8_t i = 0; i < AW20216S_PWM_REGISTER_COUNT; i++) {
aw20216s_write_register(cs_pin, AW20216S_PAGE_SCALING, i, AW20216S_SCALING_MAX);
}
}
static inline void aw20216s_init_current_limit(pin_t cs_pin) {
// Push config
aw20216s_write_register(cs_pin, AW20216S_PAGE_FUNCTION, AW20216S_REG_GLOBALCURRENT, AW20216S_GLOBAL_CURRENT_MAX);
}
static inline void aw20216s_soft_enable(pin_t cs_pin) {
// Push config
aw20216s_write_register(cs_pin, AW20216S_PAGE_FUNCTION, AW20216S_REG_CONFIGURATION, AW20216S_CONFIG_DEFAULT | AW20216S_CHIPEN);
}
static inline void aw20216s_auto_lowpower(pin_t cs_pin) {
aw20216s_write_register(cs_pin, AW20216S_PAGE_FUNCTION, AW20216S_REG_MIXFUNCTION, AW20216S_MIXCR_DEFAULT | AW20216S_LPEN);
}
void aw20216s_init(pin_t cs_pin, pin_t en_pin) {
setPinOutput(en_pin);
writePinHigh(en_pin);
aw20216s_soft_reset(cs_pin);
wait_ms(2);
// Drivers should start with all scaling and PWM registers as off
aw20216s_init_current_limit(cs_pin);
aw20216s_init_scaling(cs_pin);
aw20216s_soft_enable(cs_pin);
aw20216s_auto_lowpower(cs_pin);
}
void aw20216s_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
aw20216s_led_t led;
memcpy_P(&led, (&g_aw20216s_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 aw20216s_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
for (uint8_t i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
aw20216s_set_color(i, red, green, blue);
}
}
void aw20216s_update_pwm_buffers(pin_t cs_pin, uint8_t index) {
if (g_pwm_buffer_update_required[index]) {
aw20216s_write(cs_pin, AW20216S_PAGE_PWM, 0, g_pwm_buffer[index], AW20216S_PWM_REGISTER_COUNT);
}
g_pwm_buffer_update_required[index] = false;
}

286
drivers/led/aw20216s.h Normal file
View File

@ -0,0 +1,286 @@
/* Copyright 2021 Jasper Chan (Gigahawk)
*
* 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>
#include <stdbool.h>
#include "progmem.h"
#include "gpio.h"
// ======== DEPRECATED DEFINES - DO NOT USE ========
#ifdef DRIVER_COUNT
# define AW20216S_DRIVER_COUNT DRIVER_COUNT
#endif
#ifdef AW_SCALING_MAX
# define AW20216S_SCALING_MAX AW_SCALING_MAX
#endif
#ifdef AW_GLOBAL_CURRENT_MAX
# define AW20216S_GLOBAL_CURRENT_MAX AW_GLOBAL_CURRENT_MAX
#endif
#ifdef AW_SPI_MODE
# define AW20216S_SPI_MODE AW_SPI_MODE
#endif
#ifdef AW_SPI_DIVISOR
# define AW20216S_SPI_DIVISOR AW_SPI_DIVISOR
#endif
#ifdef DRIVER_1_CS
# define AW20216S_DRIVER_1_CS DRIVER_1_CS
#endif
#ifdef DRIVER_2_CS
# define AW20216S_DRIVER_2_CS DRIVER_2_CS
#endif
#ifdef DRIVER_1_EN
# define AW20216S_DRIVER_1_EN DRIVER_1_EN
#endif
#ifdef DRIVER_2_EN
# define AW20216S_DRIVER_2_EN DRIVER_2_EN
#endif
#define aw_led aw20216s_led_t
#define g_aw_leds g_aw20216s_leds
// ========
typedef struct aw20216s_led_t {
uint8_t driver : 2;
uint8_t r;
uint8_t g;
uint8_t b;
} aw20216s_led_t;
extern const aw20216s_led_t PROGMEM g_aw20216s_leds[RGB_MATRIX_LED_COUNT];
void aw20216s_init(pin_t cs_pin, pin_t en_pin);
void aw20216s_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
void aw20216s_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
void aw20216s_update_pwm_buffers(pin_t cs_pin, uint8_t index);
#define CS1_SW1 0x00
#define CS2_SW1 0x01
#define CS3_SW1 0x02
#define CS4_SW1 0x03
#define CS5_SW1 0x04
#define CS6_SW1 0x05
#define CS7_SW1 0x06
#define CS8_SW1 0x07
#define CS9_SW1 0x08
#define CS10_SW1 0x09
#define CS11_SW1 0x0A
#define CS12_SW1 0x0B
#define CS13_SW1 0x0C
#define CS14_SW1 0x0D
#define CS15_SW1 0x0E
#define CS16_SW1 0x0F
#define CS17_SW1 0x10
#define CS18_SW1 0x11
#define CS1_SW2 0x12
#define CS2_SW2 0x13
#define CS3_SW2 0x14
#define CS4_SW2 0x15
#define CS5_SW2 0x16
#define CS6_SW2 0x17
#define CS7_SW2 0x18
#define CS8_SW2 0x19
#define CS9_SW2 0x1A
#define CS10_SW2 0x1B
#define CS11_SW2 0x1C
#define CS12_SW2 0x1D
#define CS13_SW2 0x1E
#define CS14_SW2 0x1F
#define CS15_SW2 0x20
#define CS16_SW2 0x21
#define CS17_SW2 0x22
#define CS18_SW2 0x23
#define CS1_SW3 0x24
#define CS2_SW3 0x25
#define CS3_SW3 0x26
#define CS4_SW3 0x27
#define CS5_SW3 0x28
#define CS6_SW3 0x29
#define CS7_SW3 0x2A
#define CS8_SW3 0x2B
#define CS9_SW3 0x2C
#define CS10_SW3 0x2D
#define CS11_SW3 0x2E
#define CS12_SW3 0x2F
#define CS13_SW3 0x30
#define CS14_SW3 0x31
#define CS15_SW3 0x32
#define CS16_SW3 0x33
#define CS17_SW3 0x34
#define CS18_SW3 0x35
#define CS1_SW4 0x36
#define CS2_SW4 0x37
#define CS3_SW4 0x38
#define CS4_SW4 0x39
#define CS5_SW4 0x3A
#define CS6_SW4 0x3B
#define CS7_SW4 0x3C
#define CS8_SW4 0x3D
#define CS9_SW4 0x3E
#define CS10_SW4 0x3F
#define CS11_SW4 0x40
#define CS12_SW4 0x41
#define CS13_SW4 0x42
#define CS14_SW4 0x43
#define CS15_SW4 0x44
#define CS16_SW4 0x45
#define CS17_SW4 0x46
#define CS18_SW4 0x47
#define CS1_SW5 0x48
#define CS2_SW5 0x49
#define CS3_SW5 0x4A
#define CS4_SW5 0x4B
#define CS5_SW5 0x4C
#define CS6_SW5 0x4D
#define CS7_SW5 0x4E
#define CS8_SW5 0x4F
#define CS9_SW5 0x50
#define CS10_SW5 0x51
#define CS11_SW5 0x52
#define CS12_SW5 0x53
#define CS13_SW5 0x54
#define CS14_SW5 0x55
#define CS15_SW5 0x56
#define CS16_SW5 0x57
#define CS17_SW5 0x58
#define CS18_SW5 0x59
#define CS1_SW6 0x5A
#define CS2_SW6 0x5B
#define CS3_SW6 0x5C
#define CS4_SW6 0x5D
#define CS5_SW6 0x5E
#define CS6_SW6 0x5F
#define CS7_SW6 0x60
#define CS8_SW6 0x61
#define CS9_SW6 0x62
#define CS10_SW6 0x63
#define CS11_SW6 0x64
#define CS12_SW6 0x65
#define CS13_SW6 0x66
#define CS14_SW6 0x67
#define CS15_SW6 0x68
#define CS16_SW6 0x69
#define CS17_SW6 0x6A
#define CS18_SW6 0x6B
#define CS1_SW7 0x6C
#define CS2_SW7 0x6D
#define CS3_SW7 0x6E
#define CS4_SW7 0x6F
#define CS5_SW7 0x70
#define CS6_SW7 0x71
#define CS7_SW7 0x72
#define CS8_SW7 0x73
#define CS9_SW7 0x74
#define CS10_SW7 0x75
#define CS11_SW7 0x76
#define CS12_SW7 0x77
#define CS13_SW7 0x78
#define CS14_SW7 0x79
#define CS15_SW7 0x7A
#define CS16_SW7 0x7B
#define CS17_SW7 0x7C
#define CS18_SW7 0x7D
#define CS1_SW8 0x7E
#define CS2_SW8 0x7F
#define CS3_SW8 0x80
#define CS4_SW8 0x81
#define CS5_SW8 0x82
#define CS6_SW8 0x83
#define CS7_SW8 0x84
#define CS8_SW8 0x85
#define CS9_SW8 0x86
#define CS10_SW8 0x87
#define CS11_SW8 0x88
#define CS12_SW8 0x89
#define CS13_SW8 0x8A
#define CS14_SW8 0x8B
#define CS15_SW8 0x8C
#define CS16_SW8 0x8D
#define CS17_SW8 0x8E
#define CS18_SW8 0x8F
#define CS1_SW9 0x90
#define CS2_SW9 0x91
#define CS3_SW9 0x92
#define CS4_SW9 0x93
#define CS5_SW9 0x94
#define CS6_SW9 0x95
#define CS7_SW9 0x96
#define CS8_SW9 0x97
#define CS9_SW9 0x98
#define CS10_SW9 0x99
#define CS11_SW9 0x9A
#define CS12_SW9 0x9B
#define CS13_SW9 0x9C
#define CS14_SW9 0x9D
#define CS15_SW9 0x9E
#define CS16_SW9 0x9F
#define CS17_SW9 0xA0
#define CS18_SW9 0xA1
#define CS1_SW10 0xA2
#define CS2_SW10 0xA3
#define CS3_SW10 0xA4
#define CS4_SW10 0xA5
#define CS5_SW10 0xA6
#define CS6_SW10 0xA7
#define CS7_SW10 0xA8
#define CS8_SW10 0xA9
#define CS9_SW10 0xAA
#define CS10_SW10 0xAB
#define CS11_SW10 0xAC
#define CS12_SW10 0xAD
#define CS13_SW10 0xAE
#define CS14_SW10 0xAF
#define CS15_SW10 0xB0
#define CS16_SW10 0xB1
#define CS17_SW10 0xB2
#define CS18_SW10 0xB3
#define CS1_SW11 0xB4
#define CS2_SW11 0xB5
#define CS3_SW11 0xB6
#define CS4_SW11 0xB7
#define CS5_SW11 0xB8
#define CS6_SW11 0xB9
#define CS7_SW11 0xBA
#define CS8_SW11 0xBB
#define CS9_SW11 0xBC
#define CS10_SW11 0xBD
#define CS11_SW11 0xBE
#define CS12_SW11 0xBF
#define CS13_SW11 0xC0
#define CS14_SW11 0xC1
#define CS15_SW11 0xC2
#define CS16_SW11 0xC3
#define CS17_SW11 0xC4
#define CS18_SW11 0xC5
#define CS1_SW12 0xC6
#define CS2_SW12 0xC7
#define CS3_SW12 0xC8
#define CS4_SW12 0xC9
#define CS5_SW12 0xCA
#define CS6_SW12 0xCB
#define CS7_SW12 0xCC
#define CS8_SW12 0xCD
#define CS9_SW12 0xCE
#define CS10_SW12 0xCF
#define CS11_SW12 0xD0
#define CS12_SW12 0xD1
#define CS13_SW12 0xD2
#define CS14_SW12 0xD3
#define CS15_SW12 0xD4
#define CS16_SW12 0xD5
#define CS17_SW12 0xD6
#define CS18_SW12 0xD7

View File

@ -1,221 +0,0 @@
/* Copyright 2021 @ Keychron (https://www.keychron.com)
*
* 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 "ckled2001-simple.h"
#include "i2c_master.h"
#include "wait.h"
#ifndef CKLED2001_TIMEOUT
# define CKLED2001_TIMEOUT 100
#endif
#ifndef CKLED2001_PERSISTENCE
# define CKLED2001_PERSISTENCE 0
#endif
#ifndef PHASE_CHANNEL
# define PHASE_CHANNEL MSKPHASE_12CHANNEL
#endif
#ifndef CKLED2001_CURRENT_TUNE
# define CKLED2001_CURRENT_TUNE \
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
#endif
// Transfer buffer for TWITransmitData()
uint8_t g_twi_transfer_buffer[20];
// These buffers match the CKLED2001 PWM registers.
// 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
// probably not worth the extra complexity.
uint8_t g_pwm_buffer[DRIVER_COUNT][192];
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) {
// If the transaction fails function returns false.
g_twi_transfer_buffer[0] = reg;
g_twi_transfer_buffer[1] = data;
#if CKLED2001_PERSISTENCE > 0
for (uint8_t i = 0; i < CKLED2001_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, CKLED2001_TIMEOUT) != 0) {
return false;
}
}
#else
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, CKLED2001_TIMEOUT) != 0) {
return false;
}
#endif
return true;
}
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.
// g_twi_transfer_buffer[] is 20 bytes
// Iterate over the pwm_buffer contents at 16 byte intervals.
for (int i = 0; i < 192; i += 16) {
g_twi_transfer_buffer[0] = i;
// Copy the data from i to i+15.
// Device will auto-increment register for data after the first byte
// Thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer.
for (int j = 0; j < 16; j++) {
g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
}
#if CKLED2001_PERSISTENCE > 0
for (uint8_t i = 0; i < CKLED2001_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, CKLED2001_TIMEOUT) != 0) {
return false;
}
}
#else
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, CKLED2001_TIMEOUT) != 0) {
return false;
}
#endif
}
return true;
}
void ckled2001_init(uint8_t addr) {
// Select to 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);
// Setting internal channel pulldown/pullup
ckled2001_write_register(addr, PDU_REG, MSKSET_CA_CB_CHANNEL);
// Select number of scan phase
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);
// Setting Driving/Sinking Channel Slew Rate
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);
// Set LED CONTROL PAGE (Page 0)
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);
}
// Set PWM PAGE (Page 1)
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);
}
// 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);
for (int i = 0; i < LED_CURRENT_TUNE_LENGTH; 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);
for (int i = 0; i < LED_CONTROL_ON_OFF_LENGTH; i++) {
ckled2001_write_register(addr, i, 0xFF);
}
// Select to 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);
}
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) {
for (int i = 0; i < LED_MATRIX_LED_COUNT; i++) {
ckled2001_set_value(i, value);
}
}
void ckled2001_set_led_control_register(uint8_t index, bool value) {
ckled2001_led led;
memcpy_P(&led, (&g_ckled2001_leds[index]), sizeof(led));
uint8_t control_register = led.v / 8;
uint8_t bit_value = led.v % 8;
if (value) {
g_led_control_registers[led.driver][control_register] |= (1 << bit_value);
} else {
g_led_control_registers[led.driver][control_register] &= ~(1 << bit_value);
}
g_led_control_registers_update_required[led.driver] = true;
}
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);
// 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])) {
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) {
if (g_led_control_registers_update_required[index]) {
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]);
}
}
g_led_control_registers_update_required[index] = false;
}
void ckled2001_sw_return_normal(uint8_t addr) {
// Select to 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);
}
void ckled2001_sw_shutdown(uint8_t addr) {
// Select to 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);
// Write SW Sleep Register
ckled2001_write_register(addr, SOFTWARE_SLEEP_REG, MSKSLEEP_ENABLE);
}

View File

@ -1,337 +0,0 @@
/* Copyright 2021 @ Keychron (https://www.keychron.com)
*
* 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>
#include <stdbool.h>
#include "progmem.h"
typedef struct ckled2001_led {
uint8_t driver : 2;
uint8_t v;
} __attribute__((packed)) 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_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);
// 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_sw_return_normal(uint8_t addr);
void ckled2001_sw_shutdown(uint8_t addr);
// Registers Page Define
#define CONFIGURE_CMD_PAGE 0xFD
#define LED_CONTROL_PAGE 0x00
#define LED_PWM_PAGE 0x01
#define FUNCTION_PAGE 0x03
#define CURRENT_TUNE_PAGE 0x04
// Function Register: address 0x00
#define CONFIGURATION_REG 0x00
#define MSKSW_SHUT_DOWN_MODE (0x0 << 0)
#define MSKSW_NORMAL_MODE (0x1 << 0)
#define DRIVER_ID_REG 0x11
#define CKLED2001_ID 0x8A
#define PDU_REG 0x13
#define MSKSET_CA_CB_CHANNEL 0xAA
#define MSKCLR_CA_CB_CHANNEL 0x00
#define SCAN_PHASE_REG 0x14
#define MSKPHASE_12CHANNEL 0x00
#define MSKPHASE_11CHANNEL 0x01
#define MSKPHASE_10CHANNEL 0x02
#define MSKPHASE_9CHANNEL 0x03
#define MSKPHASE_8CHANNEL 0x04
#define MSKPHASE_7CHANNEL 0x05
#define MSKPHASE_6CHANNEL 0x06
#define MSKPHASE_5CHANNEL 0x07
#define MSKPHASE_4CHANNEL 0x08
#define MSKPHASE_3CHANNEL 0x09
#define MSKPHASE_2CHANNEL 0x0A
#define MSKPHASE_1CHANNEL 0x0B
#define SLEW_RATE_CONTROL_MODE1_REG 0x15
#define MSKPWM_DELAY_PHASE_ENABLE 0x04
#define MSKPWM_DELAY_PHASE_DISABLE 0x00
#define SLEW_RATE_CONTROL_MODE2_REG 0x16
#define MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_ENABLE 0xC0
#define MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_DISABLE 0x00
#define OPEN_SHORT_ENABLE_REG 0x17
#define MSKOPEN_DETECTION_ENABLE (0x01 << 7)
#define MSKOPEN_DETECTION_DISABLE (0x00)
#define MSKSHORT_DETECTION_ENABLE (0x01 << 6)
#define MSKSHORT_DETECTION_DISABLE (0x00)
#define OPEN_SHORT_DUTY_REG 0x18
#define OPEN_SHORT_FLAG_REG 0x19
#define MSKOPEN_DETECTION_INTERRUPT_ENABLE (0x01 << 7)
#define MSKOPEN_DETECTION_INTERRUPT_DISABLE (0x00)
#define MSKSHORT_DETECTION_INTERRUPT_ENABLE (0x01 << 6)
#define MSKSHORT_DETECTION_INTERRUPT_DISABLE (0x00)
#define SOFTWARE_SLEEP_REG 0x1A
#define MSKSLEEP_ENABLE 0x02
#define MSKSLEEP_DISABLE 0x00
// LED Control Registers
#define LED_CONTROL_ON_OFF_FIRST_ADDR 0x0
#define LED_CONTROL_ON_OFF_LAST_ADDR 0x17
#define LED_CONTROL_ON_OFF_LENGTH ((LED_CONTROL_ON_OFF_LAST_ADDR - LED_CONTROL_ON_OFF_FIRST_ADDR) + 1)
#define LED_CONTROL_OPEN_FIRST_ADDR 0x18
#define LED_CONTROL_OPEN_LAST_ADDR 0x2F
#define LED_CONTROL_OPEN_LENGTH ((LED_CONTROL_OPEN_LAST_ADDR - LED_CONTROL_OPEN_FIRST_ADDR) + 1)
#define LED_CONTROL_SHORT_FIRST_ADDR 0x30
#define LED_CONTROL_SHORT_LAST_ADDR 0x47
#define LED_CONTROL_SHORT_LENGTH ((LED_CONTROL_SHORT_LAST_ADDR - LED_CONTROL_SHORT_FIRST_ADDR) + 1)
#define LED_CONTROL_PAGE_LENGTH 0x48
// LED Control Registers
#define LED_PWM_FIRST_ADDR 0x00
#define LED_PWM_LAST_ADDR 0xBF
#define LED_PWM_LENGTH 0xC0
// Current Tune Registers
#define LED_CURRENT_TUNE_FIRST_ADDR 0x00
#define LED_CURRENT_TUNE_LAST_ADDR 0x0B
#define LED_CURRENT_TUNE_LENGTH 0x0C
#define A_1 0x00
#define A_2 0x01
#define A_3 0x02
#define A_4 0x03
#define A_5 0x04
#define A_6 0x05
#define A_7 0x06
#define A_8 0x07
#define A_9 0x08
#define A_10 0x09
#define A_11 0x0A
#define A_12 0x0B
#define A_13 0x0C
#define A_14 0x0D
#define A_15 0x0E
#define A_16 0x0F
#define B_1 0x10
#define B_2 0x11
#define B_3 0x12
#define B_4 0x13
#define B_5 0x14
#define B_6 0x15
#define B_7 0x16
#define B_8 0x17
#define B_9 0x18
#define B_10 0x19
#define B_11 0x1A
#define B_12 0x1B
#define B_13 0x1C
#define B_14 0x1D
#define B_15 0x1E
#define B_16 0x1F
#define C_1 0x20
#define C_2 0x21
#define C_3 0x22
#define C_4 0x23
#define C_5 0x24
#define C_6 0x25
#define C_7 0x26
#define C_8 0x27
#define C_9 0x28
#define C_10 0x29
#define C_11 0x2A
#define C_12 0x2B
#define C_13 0x2C
#define C_14 0x2D
#define C_15 0x2E
#define C_16 0x2F
#define D_1 0x30
#define D_2 0x31
#define D_3 0x32
#define D_4 0x33
#define D_5 0x34
#define D_6 0x35
#define D_7 0x36
#define D_8 0x37
#define D_9 0x38
#define D_10 0x39
#define D_11 0x3A
#define D_12 0x3B
#define D_13 0x3C
#define D_14 0x3D
#define D_15 0x3E
#define D_16 0x3F
#define E_1 0x40
#define E_2 0x41
#define E_3 0x42
#define E_4 0x43
#define E_5 0x44
#define E_6 0x45
#define E_7 0x46
#define E_8 0x47
#define E_9 0x48
#define E_10 0x49
#define E_11 0x4A
#define E_12 0x4B
#define E_13 0x4C
#define E_14 0x4D
#define E_15 0x4E
#define E_16 0x4F
#define F_1 0x50
#define F_2 0x51
#define F_3 0x52
#define F_4 0x53
#define F_5 0x54
#define F_6 0x55
#define F_7 0x56
#define F_8 0x57
#define F_9 0x58
#define F_10 0x59
#define F_11 0x5A
#define F_12 0x5B
#define F_13 0x5C
#define F_14 0x5D
#define F_15 0x5E
#define F_16 0x5F
#define G_1 0x60
#define G_2 0x61
#define G_3 0x62
#define G_4 0x63
#define G_5 0x64
#define G_6 0x65
#define G_7 0x66
#define G_8 0x67
#define G_9 0x68
#define G_10 0x69
#define G_11 0x6A
#define G_12 0x6B
#define G_13 0x6C
#define G_14 0x6D
#define G_15 0x6E
#define G_16 0x6F
#define H_1 0x70
#define H_2 0x71
#define H_3 0x72
#define H_4 0x73
#define H_5 0x74
#define H_6 0x75
#define H_7 0x76
#define H_8 0x77
#define H_9 0x78
#define H_10 0x79
#define H_11 0x7A
#define H_12 0x7B
#define H_13 0x7C
#define H_14 0x7D
#define H_15 0x7E
#define H_16 0x7F
#define I_1 0x80
#define I_2 0x81
#define I_3 0x82
#define I_4 0x83
#define I_5 0x84
#define I_6 0x85
#define I_7 0x86
#define I_8 0x87
#define I_9 0x88
#define I_10 0x89
#define I_11 0x8A
#define I_12 0x8B
#define I_13 0x8C
#define I_14 0x8D
#define I_15 0x8E
#define I_16 0x8F
#define J_1 0x90
#define J_2 0x91
#define J_3 0x92
#define J_4 0x93
#define J_5 0x94
#define J_6 0x95
#define J_7 0x96
#define J_8 0x97
#define J_9 0x98
#define J_10 0x99
#define J_11 0x9A
#define J_12 0x9B
#define J_13 0x9C
#define J_14 0x9D
#define J_15 0x9E
#define J_16 0x9F
#define K_1 0xA0
#define K_2 0xA1
#define K_3 0xA2
#define K_4 0xA3
#define K_5 0xA4
#define K_6 0xA5
#define K_7 0xA6
#define K_8 0xA7
#define K_9 0xA8
#define K_10 0xA9
#define K_11 0xAA
#define K_12 0xAB
#define K_13 0xAC
#define K_14 0xAD
#define K_15 0xAE
#define K_16 0xAF
#define L_1 0xB0
#define L_2 0xB1
#define L_3 0xB2
#define L_4 0xB3
#define L_5 0xB4
#define L_6 0xB5
#define L_7 0xB6
#define L_8 0xB7
#define L_9 0xB8
#define L_10 0xB9
#define L_11 0xBA
#define L_12 0xBB
#define L_13 0xBC
#define L_14 0xBD
#define L_15 0xBE
#define L_16 0xBF

View File

@ -1,236 +0,0 @@
/* Copyright 2021 @ Keychron (https://www.keychron.com)
*
* 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 "ckled2001.h"
#include "i2c_master.h"
#include "wait.h"
#ifndef CKLED2001_TIMEOUT
# define CKLED2001_TIMEOUT 100
#endif
#ifndef CKLED2001_PERSISTENCE
# define CKLED2001_PERSISTENCE 0
#endif
#ifndef PHASE_CHANNEL
# define PHASE_CHANNEL MSKPHASE_12CHANNEL
#endif
#ifndef CKLED2001_CURRENT_TUNE
# define CKLED2001_CURRENT_TUNE \
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
#endif
// Transfer buffer for TWITransmitData()
uint8_t g_twi_transfer_buffer[65];
// These buffers match the CKLED2001 PWM registers.
// 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
// probably not worth the extra complexity.
uint8_t g_pwm_buffer[DRIVER_COUNT][192];
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) {
// If the transaction fails function returns false.
g_twi_transfer_buffer[0] = reg;
g_twi_transfer_buffer[1] = data;
#if CKLED2001_PERSISTENCE > 0
for (uint8_t i = 0; i < CKLED2001_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, CKLED2001_TIMEOUT) != 0) {
return false;
}
}
#else
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, CKLED2001_TIMEOUT) != 0) {
return false;
}
#endif
return true;
}
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.
// Iterate over the pwm_buffer contents at 64 byte intervals.
for (uint8_t i = 0; i < 192; i += 64) {
g_twi_transfer_buffer[0] = i;
// Copy the data from i to i+63.
// Device will auto-increment register for data after the first byte
// Thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer.
for (uint8_t j = 0; j < 64; j++) {
g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
}
#if CKLED2001_PERSISTENCE > 0
for (uint8_t i = 0; i < CKLED2001_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 65, CKLED2001_TIMEOUT) != 0) {
return false;
}
}
#else
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 65, CKLED2001_TIMEOUT) != 0) {
return false;
}
#endif
}
return true;
}
void ckled2001_init(uint8_t addr) {
// Select to 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);
// Setting internal channel pulldown/pullup
ckled2001_write_register(addr, PDU_REG, MSKSET_CA_CB_CHANNEL);
// Select number of scan phase
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);
// Setting Driving/Sinking Channel Slew Rate
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);
// Set LED CONTROL PAGE (Page 0)
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);
}
// Set PWM PAGE (Page 1)
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);
}
// 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);
for (int i = 0; i < LED_CURRENT_TUNE_LENGTH; 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);
for (int i = 0; i < LED_CONTROL_ON_OFF_LENGTH; i++) {
ckled2001_write_register(addr, i, 0xFF);
}
// Select to 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);
}
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;
g_pwm_buffer_update_required[led.driver] = true;
}
}
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);
}
}
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));
uint8_t control_register_r = led.r / 8;
uint8_t control_register_g = led.g / 8;
uint8_t control_register_b = led.b / 8;
uint8_t bit_r = led.r % 8;
uint8_t bit_g = led.g % 8;
uint8_t bit_b = led.b % 8;
if (red) {
g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r);
} else {
g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r);
}
if (green) {
g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g);
} else {
g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g);
}
if (blue) {
g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b);
} else {
g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);
}
g_led_control_registers_update_required[led.driver] = true;
}
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);
// 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])) {
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) {
if (g_led_control_registers_update_required[index]) {
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]);
}
}
g_led_control_registers_update_required[index] = false;
}
void ckled2001_sw_return_normal(uint8_t addr) {
// Select to 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);
}
void ckled2001_sw_shutdown(uint8_t addr) {
// Select to 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);
// Write SW Sleep Register
ckled2001_write_register(addr, SOFTWARE_SLEEP_REG, MSKSLEEP_ENABLE);
}

View File

@ -1,339 +0,0 @@
/* Copyright 2021 @ Keychron (https://www.keychron.com)
*
* 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>
#include <stdbool.h>
#include "progmem.h"
typedef struct ckled2001_led {
uint8_t driver : 2;
uint8_t r;
uint8_t g;
uint8_t b;
} __attribute__((packed)) 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_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);
// 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_sw_return_normal(uint8_t addr);
void ckled2001_sw_shutdown(uint8_t addr);
// Registers Page Define
#define CONFIGURE_CMD_PAGE 0xFD
#define LED_CONTROL_PAGE 0x00
#define LED_PWM_PAGE 0x01
#define FUNCTION_PAGE 0x03
#define CURRENT_TUNE_PAGE 0x04
// Function Register: address 0x00
#define CONFIGURATION_REG 0x00
#define MSKSW_SHUT_DOWN_MODE (0x0 << 0)
#define MSKSW_NORMAL_MODE (0x1 << 0)
#define DRIVER_ID_REG 0x11
#define CKLED2001_ID 0x8A
#define PDU_REG 0x13
#define MSKSET_CA_CB_CHANNEL 0xAA
#define MSKCLR_CA_CB_CHANNEL 0x00
#define SCAN_PHASE_REG 0x14
#define MSKPHASE_12CHANNEL 0x00
#define MSKPHASE_11CHANNEL 0x01
#define MSKPHASE_10CHANNEL 0x02
#define MSKPHASE_9CHANNEL 0x03
#define MSKPHASE_8CHANNEL 0x04
#define MSKPHASE_7CHANNEL 0x05
#define MSKPHASE_6CHANNEL 0x06
#define MSKPHASE_5CHANNEL 0x07
#define MSKPHASE_4CHANNEL 0x08
#define MSKPHASE_3CHANNEL 0x09
#define MSKPHASE_2CHANNEL 0x0A
#define MSKPHASE_1CHANNEL 0x0B
#define SLEW_RATE_CONTROL_MODE1_REG 0x15
#define MSKPWM_DELAY_PHASE_ENABLE 0x04
#define MSKPWM_DELAY_PHASE_DISABLE 0x00
#define SLEW_RATE_CONTROL_MODE2_REG 0x16
#define MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_ENABLE 0xC0
#define MSKDRIVING_SINKING_CHHANNEL_SLEWRATE_DISABLE 0x00
#define OPEN_SHORT_ENABLE_REG 0x17
#define MSKOPEN_DETECTION_ENABLE (0x01 << 7)
#define MSKOPEN_DETECTION_DISABLE (0x00)
#define MSKSHORT_DETECTION_ENABLE (0x01 << 6)
#define MSKSHORT_DETECTION_DISABLE (0x00)
#define OPEN_SHORT_DUTY_REG 0x18
#define OPEN_SHORT_FLAG_REG 0x19
#define MSKOPEN_DETECTION_INTERRUPT_ENABLE (0x01 << 7)
#define MSKOPEN_DETECTION_INTERRUPT_DISABLE (0x00)
#define MSKSHORT_DETECTION_INTERRUPT_ENABLE (0x01 << 6)
#define MSKSHORT_DETECTION_INTERRUPT_DISABLE (0x00)
#define SOFTWARE_SLEEP_REG 0x1A
#define MSKSLEEP_ENABLE 0x02
#define MSKSLEEP_DISABLE 0x00
// LED Control Registers
#define LED_CONTROL_ON_OFF_FIRST_ADDR 0x0
#define LED_CONTROL_ON_OFF_LAST_ADDR 0x17
#define LED_CONTROL_ON_OFF_LENGTH ((LED_CONTROL_ON_OFF_LAST_ADDR - LED_CONTROL_ON_OFF_FIRST_ADDR) + 1)
#define LED_CONTROL_OPEN_FIRST_ADDR 0x18
#define LED_CONTROL_OPEN_LAST_ADDR 0x2F
#define LED_CONTROL_OPEN_LENGTH ((LED_CONTROL_OPEN_LAST_ADDR - LED_CONTROL_OPEN_FIRST_ADDR) + 1)
#define LED_CONTROL_SHORT_FIRST_ADDR 0x30
#define LED_CONTROL_SHORT_LAST_ADDR 0x47
#define LED_CONTROL_SHORT_LENGTH ((LED_CONTROL_SHORT_LAST_ADDR - LED_CONTROL_SHORT_FIRST_ADDR) + 1)
#define LED_CONTROL_PAGE_LENGTH 0x48
// LED Control Registers
#define LED_PWM_FIRST_ADDR 0x00
#define LED_PWM_LAST_ADDR 0xBF
#define LED_PWM_LENGTH 0xC0
// Current Tune Registers
#define LED_CURRENT_TUNE_FIRST_ADDR 0x00
#define LED_CURRENT_TUNE_LAST_ADDR 0x0B
#define LED_CURRENT_TUNE_LENGTH 0x0C
#define A_1 0x00
#define A_2 0x01
#define A_3 0x02
#define A_4 0x03
#define A_5 0x04
#define A_6 0x05
#define A_7 0x06
#define A_8 0x07
#define A_9 0x08
#define A_10 0x09
#define A_11 0x0A
#define A_12 0x0B
#define A_13 0x0C
#define A_14 0x0D
#define A_15 0x0E
#define A_16 0x0F
#define B_1 0x10
#define B_2 0x11
#define B_3 0x12
#define B_4 0x13
#define B_5 0x14
#define B_6 0x15
#define B_7 0x16
#define B_8 0x17
#define B_9 0x18
#define B_10 0x19
#define B_11 0x1A
#define B_12 0x1B
#define B_13 0x1C
#define B_14 0x1D
#define B_15 0x1E
#define B_16 0x1F
#define C_1 0x20
#define C_2 0x21
#define C_3 0x22
#define C_4 0x23
#define C_5 0x24
#define C_6 0x25
#define C_7 0x26
#define C_8 0x27
#define C_9 0x28
#define C_10 0x29
#define C_11 0x2A
#define C_12 0x2B
#define C_13 0x2C
#define C_14 0x2D
#define C_15 0x2E
#define C_16 0x2F
#define D_1 0x30
#define D_2 0x31
#define D_3 0x32
#define D_4 0x33
#define D_5 0x34
#define D_6 0x35
#define D_7 0x36
#define D_8 0x37
#define D_9 0x38
#define D_10 0x39
#define D_11 0x3A
#define D_12 0x3B
#define D_13 0x3C
#define D_14 0x3D
#define D_15 0x3E
#define D_16 0x3F
#define E_1 0x40
#define E_2 0x41
#define E_3 0x42
#define E_4 0x43
#define E_5 0x44
#define E_6 0x45
#define E_7 0x46
#define E_8 0x47
#define E_9 0x48
#define E_10 0x49
#define E_11 0x4A
#define E_12 0x4B
#define E_13 0x4C
#define E_14 0x4D
#define E_15 0x4E
#define E_16 0x4F
#define F_1 0x50
#define F_2 0x51
#define F_3 0x52
#define F_4 0x53
#define F_5 0x54
#define F_6 0x55
#define F_7 0x56
#define F_8 0x57
#define F_9 0x58
#define F_10 0x59
#define F_11 0x5A
#define F_12 0x5B
#define F_13 0x5C
#define F_14 0x5D
#define F_15 0x5E
#define F_16 0x5F
#define G_1 0x60
#define G_2 0x61
#define G_3 0x62
#define G_4 0x63
#define G_5 0x64
#define G_6 0x65
#define G_7 0x66
#define G_8 0x67
#define G_9 0x68
#define G_10 0x69
#define G_11 0x6A
#define G_12 0x6B
#define G_13 0x6C
#define G_14 0x6D
#define G_15 0x6E
#define G_16 0x6F
#define H_1 0x70
#define H_2 0x71
#define H_3 0x72
#define H_4 0x73
#define H_5 0x74
#define H_6 0x75
#define H_7 0x76
#define H_8 0x77
#define H_9 0x78
#define H_10 0x79
#define H_11 0x7A
#define H_12 0x7B
#define H_13 0x7C
#define H_14 0x7D
#define H_15 0x7E
#define H_16 0x7F
#define I_1 0x80
#define I_2 0x81
#define I_3 0x82
#define I_4 0x83
#define I_5 0x84
#define I_6 0x85
#define I_7 0x86
#define I_8 0x87
#define I_9 0x88
#define I_10 0x89
#define I_11 0x8A
#define I_12 0x8B
#define I_13 0x8C
#define I_14 0x8D
#define I_15 0x8E
#define I_16 0x8F
#define J_1 0x90
#define J_2 0x91
#define J_3 0x92
#define J_4 0x93
#define J_5 0x94
#define J_6 0x95
#define J_7 0x96
#define J_8 0x97
#define J_9 0x98
#define J_10 0x99
#define J_11 0x9A
#define J_12 0x9B
#define J_13 0x9C
#define J_14 0x9D
#define J_15 0x9E
#define J_16 0x9F
#define K_1 0xA0
#define K_2 0xA1
#define K_3 0xA2
#define K_4 0xA3
#define K_5 0xA4
#define K_6 0xA5
#define K_7 0xA6
#define K_8 0xA7
#define K_9 0xA8
#define K_10 0xA9
#define K_11 0xAA
#define K_12 0xAB
#define K_13 0xAC
#define K_14 0xAD
#define K_15 0xAE
#define K_16 0xAF
#define L_1 0xB0
#define L_2 0xB1
#define L_3 0xB2
#define L_4 0xB3
#define L_5 0xB4
#define L_6 0xB5
#define L_7 0xB6
#define L_8 0xB7
#define L_9 0xB8
#define L_10 0xB9
#define L_11 0xBA
#define L_12 0xBB
#define L_13 0xBC
#define L_14 0xBD
#define L_15 0xBE
#define L_16 0xBF

View File

@ -0,0 +1,143 @@
/* Copyright 2018 Jason Williams (Wilba)
*
* 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 "is31fl3218.h"
#include <string.h>
#include "i2c_master.h"
// These are the register addresses
#define IS31FL3218_REG_SHUTDOWN 0x00
#define IS31FL3218_REG_PWM 0x01
#define IS31FL3218_REG_CONTROL 0x13
#define IS31FL3218_REG_UPDATE 0x16
#define IS31FL3218_REG_RESET 0x17
#ifndef IS31FL3218_I2C_TIMEOUT
# define IS31FL3218_I2C_TIMEOUT 100
#endif
#ifndef IS31FL3218_I2C_PERSISTENCE
# define IS31FL3218_I2C_PERSISTENCE 0
#endif
// Reusable buffer for transfers
uint8_t g_twi_transfer_buffer[20];
// IS31FL3218 has 18 PWM outputs and a fixed I2C address, so no chaining.
uint8_t g_pwm_buffer[18];
bool g_pwm_buffer_update_required = false;
uint8_t g_led_control_registers[3] = {0};
bool g_led_control_registers_update_required = false;
void is31fl3218_write_register(uint8_t reg, uint8_t data) {
g_twi_transfer_buffer[0] = reg;
g_twi_transfer_buffer[1] = data;
#if IS31FL3218_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3218_I2C_PERSISTENCE; i++) {
if (i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 2, IS31FL3218_I2C_TIMEOUT) == 0) break;
}
#else
i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 2, IS31FL3218_I2C_TIMEOUT);
#endif
}
void is31fl3218_write_pwm_buffer(uint8_t *pwm_buffer) {
g_twi_transfer_buffer[0] = IS31FL3218_REG_PWM;
memcpy(g_twi_transfer_buffer + 1, pwm_buffer, 18);
#if IS31FL3218_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3218_I2C_PERSISTENCE; i++) {
i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 19, IS31FL3218_I2C_TIMEOUT);
}
#else
i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 19, IS31FL3218_I2C_TIMEOUT);
#endif
}
void is31fl3218_init(void) {
// In case we ever want to reinitialize (?)
is31fl3218_write_register(IS31FL3218_REG_RESET, 0x00);
// Turn off software shutdown
is31fl3218_write_register(IS31FL3218_REG_SHUTDOWN, 0x01);
// Set all PWM values to zero
for (uint8_t i = 0; i < 18; i++) {
is31fl3218_write_register(IS31FL3218_REG_PWM + i, 0x00);
}
// turn off all LEDs in the LED control register
for (uint8_t i = 0; i < 3; i++) {
is31fl3218_write_register(IS31FL3218_REG_CONTROL + i, 0x00);
}
// Load PWM registers and LED Control register data
is31fl3218_write_register(IS31FL3218_REG_UPDATE, 0x01);
}
void is31fl3218_set_value(int index, uint8_t value) {
is31fl3218_led_t led;
if (index >= 0 && index < LED_MATRIX_LED_COUNT) {
memcpy_P(&led, (&g_is31fl3218_leds[index]), sizeof(led));
}
if (g_pwm_buffer[led.v - IS31FL3218_REG_PWM] == value) {
return;
}
g_pwm_buffer[led.v - IS31FL3218_REG_PWM] = value;
g_pwm_buffer_update_required = true;
}
void is31fl3218_set_value_all(uint8_t value) {
for (int i = 0; i < LED_MATRIX_LED_COUNT; i++) {
is31fl3218_set_value(i, value);
}
}
void is31fl3218_set_led_control_register(uint8_t index, bool value) {
is31fl3218_led_t led;
memcpy_P(&led, (&g_is31fl3218_leds[index]), sizeof(led));
uint8_t control_register = (led.v - IS31FL3218_REG_PWM) / 6;
uint8_t bit_value = (led.v - IS31FL3218_REG_PWM) % 6;
if (value) {
g_led_control_registers[control_register] |= (1 << bit_value);
} else {
g_led_control_registers[control_register] &= ~(1 << bit_value);
}
g_led_control_registers_update_required = true;
}
void is31fl3218_update_pwm_buffers(void) {
if (g_pwm_buffer_update_required) {
is31fl3218_write_pwm_buffer(g_pwm_buffer);
// Load PWM registers and LED Control register data
is31fl3218_write_register(IS31FL3218_REG_UPDATE, 0x01);
g_pwm_buffer_update_required = false;
}
}
void is31fl3218_update_led_control_registers(void) {
if (g_led_control_registers_update_required) {
for (int i = 0; i < 3; i++) {
is31fl3218_write_register(IS31FL3218_REG_CONTROL + i, g_led_control_registers[i]);
}
g_led_control_registers_update_required = false;
}
}

View File

@ -0,0 +1,60 @@
/* Copyright 2018 Jason Williams (Wilba)
*
* 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>
#include <stdbool.h>
#include "progmem.h"
#define IS31FL3218_I2C_ADDRESS 0x54
typedef struct is31fl3218_led_t {
uint8_t v;
} __attribute__((packed)) is31fl3218_led_t;
extern const is31fl3218_led_t PROGMEM g_is31fl3218_leds[LED_MATRIX_LED_COUNT];
void is31fl3218_init(void);
void is31fl3218_set_value(int index, uint8_t value);
void is31fl3218_set_value_all(uint8_t value);
void is31fl3218_set_led_control_register(uint8_t index, bool value);
void is31fl3218_update_pwm_buffers(void);
void is31fl3218_update_led_control_registers(void);
#define OUT1 0x01
#define OUT2 0x02
#define OUT3 0x03
#define OUT4 0x04
#define OUT5 0x05
#define OUT6 0x06
#define OUT7 0x07
#define OUT8 0x08
#define OUT9 0x09
#define OUT10 0x0A
#define OUT11 0x0B
#define OUT12 0x0C
#define OUT13 0x0D
#define OUT14 0x0E
#define OUT15 0x0F
#define OUT16 0x10
#define OUT17 0x11
#define OUT18 0x12

View File

@ -14,84 +14,146 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "is31fl3218.h"
#include <string.h>
#include "i2c_master.h"
// This is the full 8-bit address
#define ISSI_ADDRESS 0b10101000
// These are the register addresses
#define ISSI_REG_SHUTDOWN 0x00
#define ISSI_REG_PWM 0x01
#define ISSI_REG_CONTROL 0x13
#define ISSI_REG_UPDATE 0x16
#define ISSI_REG_RESET 0x17
#define IS31FL3218_REG_SHUTDOWN 0x00
#define IS31FL3218_REG_PWM 0x01
#define IS31FL3218_REG_CONTROL 0x13
#define IS31FL3218_REG_UPDATE 0x16
#define IS31FL3218_REG_RESET 0x17
// Default timeout if no I2C response
#define ISSI_TIMEOUT 100
#ifndef IS31FL3218_I2C_TIMEOUT
# define IS31FL3218_I2C_TIMEOUT 100
#endif
#ifndef IS31FL3218_I2C_PERSISTENCE
# define IS31FL3218_I2C_PERSISTENCE 0
#endif
// Reusable buffer for transfers
uint8_t g_twi_transfer_buffer[20];
// IS31FL3218 has 18 PWM outputs and a fixed I2C address, so no chaining.
// If used as RGB LED driver, LEDs are assigned RGB,RGB,RGB,RGB,RGB,RGB
uint8_t g_pwm_buffer[18];
bool g_pwm_buffer_update_required = false;
uint8_t g_led_control_registers[3] = {0};
bool g_led_control_registers_update_required = false;
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);
#if IS31FL3218_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3218_I2C_PERSISTENCE; i++) {
if (i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 2, IS31FL3218_I2C_TIMEOUT) == 0) break;
}
#else
i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 2, IS31FL3218_I2C_TIMEOUT);
#endif
}
void is31fl3218_write_pwm_buffer(uint8_t *pwm_buffer) {
g_twi_transfer_buffer[0] = ISSI_REG_PWM;
g_twi_transfer_buffer[0] = IS31FL3218_REG_PWM;
memcpy(g_twi_transfer_buffer + 1, pwm_buffer, 18);
i2c_transmit(ISSI_ADDRESS, g_twi_transfer_buffer, 19, ISSI_TIMEOUT);
#if IS31FL3218_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3218_I2C_PERSISTENCE; i++) {
i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 19, IS31FL3218_I2C_TIMEOUT);
}
#else
i2c_transmit(IS31FL3218_I2C_ADDRESS << 1, g_twi_transfer_buffer, 19, IS31FL3218_I2C_TIMEOUT);
#endif
}
void is31fl3218_init(void) {
// In case we ever want to reinitialize (?)
is31fl3218_write_register(ISSI_REG_RESET, 0x00);
is31fl3218_write_register(IS31FL3218_REG_RESET, 0x00);
// Turn off software shutdown
is31fl3218_write_register(ISSI_REG_SHUTDOWN, 0x01);
is31fl3218_write_register(IS31FL3218_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(IS31FL3218_REG_PWM + i, 0x00);
}
// Enable all channels
// turn off all LEDs in the LED control register
for (uint8_t i = 0; i < 3; i++) {
is31fl3218_write_register(ISSI_REG_CONTROL + i, 0b00111111);
is31fl3218_write_register(IS31FL3218_REG_CONTROL + i, 0x00);
}
// Load PWM registers and LED Control register data
is31fl3218_write_register(ISSI_REG_UPDATE, 0x01);
is31fl3218_write_register(IS31FL3218_REG_UPDATE, 0x01);
}
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) {
is31fl3218_led_t led;
if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
memcpy_P(&led, (&g_is31fl3218_leds[index]), sizeof(led));
}
if (g_pwm_buffer[led.r - IS31FL3218_REG_PWM] == red && g_pwm_buffer[led.g - IS31FL3218_REG_PWM] == green && g_pwm_buffer[led.b - IS31FL3218_REG_PWM] == 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;
g_pwm_buffer[led.r - IS31FL3218_REG_PWM] = red;
g_pwm_buffer[led.g - IS31FL3218_REG_PWM] = green;
g_pwm_buffer[led.b - IS31FL3218_REG_PWM] = blue;
g_pwm_buffer_update_required = true;
}
void is31fl3218_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
for (int i = 0; i < 6; i++) {
for (int i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
is31fl3218_set_color(i, red, green, blue);
}
}
void is31fl3218_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
is31fl3218_led_t led;
memcpy_P(&led, (&g_is31fl3218_leds[index]), sizeof(led));
uint8_t control_register_r = (led.r - IS31FL3218_REG_PWM) / 6;
uint8_t control_register_g = (led.g - IS31FL3218_REG_PWM) / 6;
uint8_t control_register_b = (led.b - IS31FL3218_REG_PWM) / 6;
uint8_t bit_r = (led.r - IS31FL3218_REG_PWM) % 6;
uint8_t bit_g = (led.g - IS31FL3218_REG_PWM) % 6;
uint8_t bit_b = (led.b - IS31FL3218_REG_PWM) % 6;
if (red) {
g_led_control_registers[control_register_r] |= (1 << bit_r);
} else {
g_led_control_registers[control_register_r] &= ~(1 << bit_r);
}
if (green) {
g_led_control_registers[control_register_g] |= (1 << bit_g);
} else {
g_led_control_registers[control_register_g] &= ~(1 << bit_g);
}
if (blue) {
g_led_control_registers[control_register_b] |= (1 << bit_b);
} else {
g_led_control_registers[control_register_b] &= ~(1 << bit_b);
}
g_led_control_registers_update_required = true;
}
void is31fl3218_update_pwm_buffers(void) {
if (g_pwm_buffer_update_required) {
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(IS31FL3218_REG_UPDATE, 0x01);
g_pwm_buffer_update_required = false;
}
}
void is31fl3218_update_led_control_registers(void) {
if (g_led_control_registers_update_required) {
for (int i = 0; i < 3; i++) {
is31fl3218_write_register(IS31FL3218_REG_CONTROL + i, g_led_control_registers[i]);
}
g_led_control_registers_update_required = false;
}
g_pwm_buffer_update_required = false;
}

View File

@ -18,9 +18,45 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "progmem.h"
#define IS31FL3218_I2C_ADDRESS 0x54
typedef struct is31fl3218_led_t {
uint8_t r;
uint8_t g;
uint8_t b;
} __attribute__((packed)) is31fl3218_led_t;
extern const is31fl3218_led_t PROGMEM g_is31fl3218_leds[RGB_MATRIX_LED_COUNT];
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_set_led_control_register(uint8_t index, bool red, bool green, bool blue);
void is31fl3218_update_pwm_buffers(void);
void is31fl3218_update_led_control_registers(void);
#define OUT1 0x01
#define OUT2 0x02
#define OUT3 0x03
#define OUT4 0x04
#define OUT5 0x05
#define OUT6 0x06
#define OUT7 0x07
#define OUT8 0x08
#define OUT9 0x09
#define OUT10 0x0A
#define OUT11 0x0B
#define OUT12 0x0C
#define OUT13 0x0D
#define OUT14 0x0E
#define OUT15 0x0F
#define OUT16 0x10
#define OUT17 0x11
#define OUT18 0x12

View File

@ -18,44 +18,36 @@
*/
#include "is31fl3731-simple.h"
#include <string.h>
#include "i2c_master.h"
#include "wait.h"
// This is a 7-bit address, that gets left-shifted and bit 0
// set to 0 for write, 1 for read (as per I2C protocol)
// The address will vary depending on your wiring:
// 0b1110100 AD <-> GND
// 0b1110111 AD <-> VCC
// 0b1110101 AD <-> SCL
// 0b1110110 AD <-> SDA
#define ISSI_ADDR_DEFAULT 0x74
#define IS31FL3731_REG_CONFIG 0x00
#define IS31FL3731_REG_CONFIG_PICTUREMODE 0x00
#define IS31FL3731_REG_CONFIG_AUTOPLAYMODE 0x08
#define IS31FL3731_REG_CONFIG_AUDIOPLAYMODE 0x18
#define ISSI_REG_CONFIG 0x00
#define ISSI_REG_CONFIG_PICTUREMODE 0x00
#define ISSI_REG_CONFIG_AUTOPLAYMODE 0x08
#define ISSI_REG_CONFIG_AUDIOPLAYMODE 0x18
#define IS31FL3731_CONF_PICTUREMODE 0x00
#define IS31FL3731_CONF_AUTOFRAMEMODE 0x04
#define IS31FL3731_CONF_AUDIOMODE 0x08
#define ISSI_CONF_PICTUREMODE 0x00
#define ISSI_CONF_AUTOFRAMEMODE 0x04
#define ISSI_CONF_AUDIOMODE 0x08
#define ISSI_REG_PICTUREFRAME 0x01
#define IS31FL3731_REG_PICTUREFRAME 0x01
// Not defined in the datasheet -- See AN for IC
#define ISSI_REG_GHOST_IMAGE_PREVENTION 0xC2 // Set bit 4 to enable de-ghosting
#define IS31FL3731_REG_GHOST_IMAGE_PREVENTION 0xC2 // Set bit 4 to enable de-ghosting
#define ISSI_REG_SHUTDOWN 0x0A
#define ISSI_REG_AUDIOSYNC 0x06
#define IS31FL3731_REG_SHUTDOWN 0x0A
#define IS31FL3731_REG_AUDIOSYNC 0x06
#define ISSI_COMMANDREGISTER 0xFD
#define ISSI_BANK_FUNCTIONREG 0x0B // helpfully called 'page nine'
#define IS31FL3731_COMMANDREGISTER 0xFD
#define IS31FL3731_BANK_FUNCTIONREG 0x0B // helpfully called 'page nine'
#ifndef ISSI_TIMEOUT
# define ISSI_TIMEOUT 100
#ifndef IS31FL3731_I2C_TIMEOUT
# define IS31FL3731_I2C_TIMEOUT 100
#endif
#ifndef ISSI_PERSISTENCE
# define ISSI_PERSISTENCE 0
#ifndef IS31FL3731_I2C_PERSISTENCE
# define IS31FL3731_I2C_PERSISTENCE 0
#endif
// Transfer buffer for TWITransmitData()
@ -66,47 +58,24 @@ uint8_t g_twi_transfer_buffer[20];
// We could optimize this and take out the unused registers from these
// 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};
uint8_t g_pwm_buffer[IS31FL3731_DRIVER_COUNT][144];
bool g_pwm_buffer_update_required[IS31FL3731_DRIVER_COUNT] = {false};
/* There's probably a better way to init this... */
#if LED_DRIVER_COUNT == 1
uint8_t g_led_control_registers[LED_DRIVER_COUNT][18] = {{0}};
#elif LED_DRIVER_COUNT == 2
uint8_t g_led_control_registers[LED_DRIVER_COUNT][18] = {{0}, {0}};
#elif LED_DRIVER_COUNT == 3
uint8_t g_led_control_registers[LED_DRIVER_COUNT][18] = {{0}, {0}, {0}};
#elif LED_DRIVER_COUNT == 4
uint8_t g_led_control_registers[LED_DRIVER_COUNT][18] = {{0}, {0}, {0}, {0}};
#endif
bool g_led_control_registers_update_required[LED_DRIVER_COUNT] = {false};
// This is the bit pattern in the LED control registers
// (for matrix A, add one to register for matrix B)
//
// reg - b7 b6 b5 b4 b3 b2 b1 b0
// 0x00 - R08,R07,R06,R05,R04,R03,R02,R01
// 0x02 - G08,G07,G06,G05,G04,G03,G02,R00
// 0x04 - B08,B07,B06,B05,B04,B03,G01,G00
// 0x06 - - , - , - , - , - ,B02,B01,B00
// 0x08 - - , - , - , - , - , - , - , -
// 0x0A - B17,B16,B15, - , - , - , - , -
// 0x0C - G17,G16,B14,B13,B12,B11,B10,B09
// 0x0E - R17,G15,G14,G13,G12,G11,G10,G09
// 0x10 - R16,R15,R14,R13,R12,R11,R10,R09
uint8_t g_led_control_registers[IS31FL3731_DRIVER_COUNT][18] = {0};
bool g_led_control_registers_update_required[IS31FL3731_DRIVER_COUNT] = {false};
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;
#if ISSI_PERSISTENCE > 0
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) == 0) {
#if IS31FL3731_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3731_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3731_I2C_TIMEOUT) == 0) {
break;
}
}
#else
i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT);
i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3731_I2C_TIMEOUT);
#endif
}
@ -125,12 +94,12 @@ void is31fl3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
// thus this sets registers 0x24-0x33, 0x34-0x43, etc. in one transfer
memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
#if ISSI_PERSISTENCE > 0
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) == 0) break;
#if IS31FL3731_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3731_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3731_I2C_TIMEOUT) == 0) break;
}
#else
i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT);
i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3731_I2C_TIMEOUT);
#endif
}
}
@ -142,26 +111,26 @@ void is31fl3731_init(uint8_t addr) {
// then disable software shutdown.
// select "function register" bank
is31fl3731_write_register(addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG);
is31fl3731_write_register(addr, IS31FL3731_COMMANDREGISTER, IS31FL3731_BANK_FUNCTIONREG);
// enable software shutdown
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, IS31FL3731_REG_SHUTDOWN, 0x00);
#ifdef IS31FL3731_DEGHOST // set to enable de-ghosting of the array
is31fl3731_write_register(addr, IS31FL3731_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, IS31FL3731_REG_CONFIG, IS31FL3731_REG_CONFIG_PICTUREMODE);
// display frame 0
is31fl3731_write_register(addr, ISSI_REG_PICTUREFRAME, 0x00);
is31fl3731_write_register(addr, IS31FL3731_REG_PICTUREFRAME, 0x00);
// audio sync off
is31fl3731_write_register(addr, ISSI_REG_AUDIOSYNC, 0x00);
is31fl3731_write_register(addr, IS31FL3731_REG_AUDIOSYNC, 0x00);
// select bank 0
is31fl3731_write_register(addr, ISSI_COMMANDREGISTER, 0);
is31fl3731_write_register(addr, IS31FL3731_COMMANDREGISTER, 0);
// turn off all LEDs in the LED control register
for (int i = 0x00; i <= 0x11; i++) {
@ -179,21 +148,21 @@ void is31fl3731_init(uint8_t addr) {
}
// select "function register" bank
is31fl3731_write_register(addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG);
is31fl3731_write_register(addr, IS31FL3731_COMMANDREGISTER, IS31FL3731_BANK_FUNCTIONREG);
// disable software shutdown
is31fl3731_write_register(addr, ISSI_REG_SHUTDOWN, 0x01);
is31fl3731_write_register(addr, IS31FL3731_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, IS31FL3731_COMMANDREGISTER, 0);
}
void is31fl3731_set_value(int index, uint8_t value) {
is31_led led;
is31fl3731_led_t led;
if (index >= 0 && index < LED_MATRIX_LED_COUNT) {
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led));
// Subtract 0x24 to get the second index of g_pwm_buffer
@ -212,8 +181,8 @@ void is31fl3731_set_value_all(uint8_t value) {
}
void is31fl3731_set_led_control_register(uint8_t index, bool value) {
is31_led led;
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
is31fl3731_led_t led;
memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led));
uint8_t control_register = (led.v - 0x24) / 8;
uint8_t bit_value = (led.v - 0x24) % 8;

View File

@ -20,15 +20,37 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "progmem.h"
typedef struct is31_led {
// ======== DEPRECATED DEFINES - DO NOT USE ========
#ifdef LED_DRIVER_COUNT
# define IS31FL3731_DRIVER_COUNT LED_DRIVER_COUNT
#endif
#ifdef ISSI_TIMEOUT
# define IS31FL3731_I2C_TIMEOUT ISSI_TIMEOUT
#endif
#ifdef ISSI_PERSISTENCE
# define IS31FL3731_I2C_PERSISTENCE ISSI_PERSISTENCE
#endif
#ifdef ISSI_3731_DEGHOST
# define IS31FL3731_DEGHOST ISSI_3731_DEGHOST
#endif
#define is31_led is31fl3731_led_t
#define g_is31_leds g_is31fl3731_leds
// ========
#define IS31FL3731_I2C_ADDRESS_GND 0x74
#define IS31FL3731_I2C_ADDRESS_SCL 0x75
#define IS31FL3731_I2C_ADDRESS_SDA 0x76
#define IS31FL3731_I2C_ADDRESS_VCC 0x77
typedef struct is31fl3731_led_t {
uint8_t driver : 2;
uint8_t v;
} __attribute__((packed)) is31_led;
} __attribute__((packed)) is31fl3731_led_t;
extern const is31_led PROGMEM g_is31_leds[LED_MATRIX_LED_COUNT];
extern const is31fl3731_led_t PROGMEM g_is31fl3731_leds[LED_MATRIX_LED_COUNT];
void is31fl3731_init(uint8_t addr);
void is31fl3731_write_register(uint8_t addr, uint8_t reg, uint8_t data);

View File

@ -17,44 +17,36 @@
*/
#include "is31fl3731.h"
#include <string.h>
#include "i2c_master.h"
#include "wait.h"
// This is a 7-bit address, that gets left-shifted and bit 0
// set to 0 for write, 1 for read (as per I2C protocol)
// The address will vary depending on your wiring:
// 0b1110100 AD <-> GND
// 0b1110111 AD <-> VCC
// 0b1110101 AD <-> SCL
// 0b1110110 AD <-> SDA
#define ISSI_ADDR_DEFAULT 0x74
#define IS31FL3731_REG_CONFIG 0x00
#define IS31FL3731_REG_CONFIG_PICTUREMODE 0x00
#define IS31FL3731_REG_CONFIG_AUTOPLAYMODE 0x08
#define IS31FL3731_REG_CONFIG_AUDIOPLAYMODE 0x18
#define ISSI_REG_CONFIG 0x00
#define ISSI_REG_CONFIG_PICTUREMODE 0x00
#define ISSI_REG_CONFIG_AUTOPLAYMODE 0x08
#define ISSI_REG_CONFIG_AUDIOPLAYMODE 0x18
#define IS31FL3731_CONF_PICTUREMODE 0x00
#define IS31FL3731_CONF_AUTOFRAMEMODE 0x04
#define IS31FL3731_CONF_AUDIOMODE 0x08
#define ISSI_CONF_PICTUREMODE 0x00
#define ISSI_CONF_AUTOFRAMEMODE 0x04
#define ISSI_CONF_AUDIOMODE 0x08
#define ISSI_REG_PICTUREFRAME 0x01
#define IS31FL3731_REG_PICTUREFRAME 0x01
// Not defined in the datasheet -- See AN for IC
#define ISSI_REG_GHOST_IMAGE_PREVENTION 0xC2 // Set bit 4 to enable de-ghosting
#define IS31FL3731_REG_GHOST_IMAGE_PREVENTION 0xC2 // Set bit 4 to enable de-ghosting
#define ISSI_REG_SHUTDOWN 0x0A
#define ISSI_REG_AUDIOSYNC 0x06
#define IS31FL3731_REG_SHUTDOWN 0x0A
#define IS31FL3731_REG_AUDIOSYNC 0x06
#define ISSI_COMMANDREGISTER 0xFD
#define ISSI_BANK_FUNCTIONREG 0x0B // helpfully called 'page nine'
#define IS31FL3731_COMMANDREGISTER 0xFD
#define IS31FL3731_BANK_FUNCTIONREG 0x0B // helpfully called 'page nine'
#ifndef ISSI_TIMEOUT
# define ISSI_TIMEOUT 100
#ifndef IS31FL3731_I2C_TIMEOUT
# define IS31FL3731_I2C_TIMEOUT 100
#endif
#ifndef ISSI_PERSISTENCE
# define ISSI_PERSISTENCE 0
#ifndef IS31FL3731_I2C_PERSISTENCE
# define IS31FL3731_I2C_PERSISTENCE 0
#endif
// Transfer buffer for TWITransmitData()
@ -65,36 +57,22 @@ uint8_t g_twi_transfer_buffer[20];
// We could optimize this and take out the unused registers from these
// 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};
uint8_t g_pwm_buffer[IS31FL3731_DRIVER_COUNT][144];
bool g_pwm_buffer_update_required[IS31FL3731_DRIVER_COUNT] = {false};
uint8_t g_led_control_registers[DRIVER_COUNT][18] = {{0}};
bool g_led_control_registers_update_required[DRIVER_COUNT] = {false};
// This is the bit pattern in the LED control registers
// (for matrix A, add one to register for matrix B)
//
// reg - b7 b6 b5 b4 b3 b2 b1 b0
// 0x00 - R08,R07,R06,R05,R04,R03,R02,R01
// 0x02 - G08,G07,G06,G05,G04,G03,G02,R00
// 0x04 - B08,B07,B06,B05,B04,B03,G01,G00
// 0x06 - - , - , - , - , - ,B02,B01,B00
// 0x08 - - , - , - , - , - , - , - , -
// 0x0A - B17,B16,B15, - , - , - , - , -
// 0x0C - G17,G16,B14,B13,B12,B11,B10,B09
// 0x0E - R17,G15,G14,G13,G12,G11,G10,G09
// 0x10 - R16,R15,R14,R13,R12,R11,R10,R09
uint8_t g_led_control_registers[IS31FL3731_DRIVER_COUNT][18] = {0};
bool g_led_control_registers_update_required[IS31FL3731_DRIVER_COUNT] = {false};
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;
#if ISSI_PERSISTENCE > 0
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) == 0) break;
#if IS31FL3731_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3731_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3731_I2C_TIMEOUT) == 0) break;
}
#else
i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT);
i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3731_I2C_TIMEOUT);
#endif
}
@ -113,12 +91,12 @@ void is31fl3731_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
// thus this sets registers 0x24-0x33, 0x34-0x43, etc. in one transfer
memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
#if ISSI_PERSISTENCE > 0
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) == 0) break;
#if IS31FL3731_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3731_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3731_I2C_TIMEOUT) == 0) break;
}
#else
i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT);
i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3731_I2C_TIMEOUT);
#endif
}
}
@ -130,26 +108,26 @@ void is31fl3731_init(uint8_t addr) {
// then disable software shutdown.
// select "function register" bank
is31fl3731_write_register(addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG);
is31fl3731_write_register(addr, IS31FL3731_COMMANDREGISTER, IS31FL3731_BANK_FUNCTIONREG);
// enable software shutdown
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, IS31FL3731_REG_SHUTDOWN, 0x00);
#ifdef IS31FL3731_DEGHOST // set to enable de-ghosting of the array
is31fl3731_write_register(addr, IS31FL3731_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, IS31FL3731_REG_CONFIG, IS31FL3731_REG_CONFIG_PICTUREMODE);
// display frame 0
is31fl3731_write_register(addr, ISSI_REG_PICTUREFRAME, 0x00);
is31fl3731_write_register(addr, IS31FL3731_REG_PICTUREFRAME, 0x00);
// audio sync off
is31fl3731_write_register(addr, ISSI_REG_AUDIOSYNC, 0x00);
is31fl3731_write_register(addr, IS31FL3731_REG_AUDIOSYNC, 0x00);
// select bank 0
is31fl3731_write_register(addr, ISSI_COMMANDREGISTER, 0);
is31fl3731_write_register(addr, IS31FL3731_COMMANDREGISTER, 0);
// turn off all LEDs in the LED control register
for (int i = 0x00; i <= 0x11; i++) {
@ -167,21 +145,21 @@ void is31fl3731_init(uint8_t addr) {
}
// select "function register" bank
is31fl3731_write_register(addr, ISSI_COMMANDREGISTER, ISSI_BANK_FUNCTIONREG);
is31fl3731_write_register(addr, IS31FL3731_COMMANDREGISTER, IS31FL3731_BANK_FUNCTIONREG);
// disable software shutdown
is31fl3731_write_register(addr, ISSI_REG_SHUTDOWN, 0x01);
is31fl3731_write_register(addr, IS31FL3731_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, IS31FL3731_COMMANDREGISTER, 0);
}
void is31fl3731_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
is31_led led;
is31fl3731_led_t led;
if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
memcpy_P(&led, (&g_is31fl3731_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) {
@ -201,8 +179,8 @@ 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) {
is31_led led;
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
is31fl3731_led_t led;
memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led));
uint8_t control_register_r = (led.r - 0x24) / 8;
uint8_t control_register_g = (led.g - 0x24) / 8;

View File

@ -19,17 +19,39 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "progmem.h"
typedef struct is31_led {
// ======== DEPRECATED DEFINES - DO NOT USE ========
#ifdef DRIVER_COUNT
# define IS31FL3731_DRIVER_COUNT DRIVER_COUNT
#endif
#ifdef ISSI_TIMEOUT
# define IS31FL3731_I2C_TIMEOUT ISSI_TIMEOUT
#endif
#ifdef ISSI_PERSISTENCE
# define IS31FL3731_I2C_PERSISTENCE ISSI_PERSISTENCE
#endif
#ifdef ISSI_3731_DEGHOST
# define IS31FL3731_DEGHOST ISSI_3731_DEGHOST
#endif
#define is31_led is31fl3731_led_t
#define g_is31_leds g_is31fl3731_leds
// ========
#define IS31FL3731_I2C_ADDRESS_GND 0x74
#define IS31FL3731_I2C_ADDRESS_SCL 0x75
#define IS31FL3731_I2C_ADDRESS_SDA 0x76
#define IS31FL3731_I2C_ADDRESS_VCC 0x77
typedef struct is31fl3731_led_t {
uint8_t driver : 2;
uint8_t r;
uint8_t g;
uint8_t b;
} __attribute__((packed)) is31_led;
} __attribute__((packed)) is31fl3731_led_t;
extern const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT];
extern const is31fl3731_led_t PROGMEM g_is31fl3731_leds[RGB_MATRIX_LED_COUNT];
void is31fl3731_init(uint8_t addr);
void is31fl3731_write_register(uint8_t addr, uint8_t reg, uint8_t data);

View File

@ -19,59 +19,48 @@
*/
#include "is31fl3733-simple.h"
#include <string.h>
#include "i2c_master.h"
#include "wait.h"
// This is a 7-bit address, that gets left-shifted and bit 0
// set to 0 for write, 1 for read (as per I2C protocol)
// The address will vary depending on your wiring:
// 00 <-> GND
// 01 <-> SCL
// 10 <-> SDA
// 11 <-> VCC
// ADDR1 represents A1:A0 of the 7-bit address.
// ADDR2 represents A3:A2 of the 7-bit address.
// The result is: 0b101(ADDR2)(ADDR1)
#define ISSI_ADDR_DEFAULT 0x50
#define IS31FL3733_COMMANDREGISTER 0xFD
#define IS31FL3733_COMMANDREGISTER_WRITELOCK 0xFE
#define IS31FL3733_INTERRUPTMASKREGISTER 0xF0
#define IS31FL3733_INTERRUPTSTATUSREGISTER 0xF1
#define ISSI_COMMANDREGISTER 0xFD
#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE
#define ISSI_INTERRUPTMASKREGISTER 0xF0
#define ISSI_INTERRUPTSTATUSREGISTER 0xF1
#define IS31FL3733_PAGE_LEDCONTROL 0x00 // PG0
#define IS31FL3733_PAGE_PWM 0x01 // PG1
#define IS31FL3733_PAGE_AUTOBREATH 0x02 // PG2
#define IS31FL3733_PAGE_FUNCTION 0x03 // PG3
#define ISSI_PAGE_LEDCONTROL 0x00 // PG0
#define ISSI_PAGE_PWM 0x01 // PG1
#define ISSI_PAGE_AUTOBREATH 0x02 // PG2
#define ISSI_PAGE_FUNCTION 0x03 // PG3
#define IS31FL3733_REG_CONFIGURATION 0x00 // PG3
#define IS31FL3733_REG_GLOBALCURRENT 0x01 // PG3
#define IS31FL3733_REG_RESET 0x11 // PG3
#define IS31FL3733_REG_SWPULLUP 0x0F // PG3
#define IS31FL3733_REG_CSPULLUP 0x10 // PG3
#define ISSI_REG_CONFIGURATION 0x00 // PG3
#define ISSI_REG_GLOBALCURRENT 0x01 // PG3
#define ISSI_REG_RESET 0x11 // PG3
#define ISSI_REG_SWPULLUP 0x0F // PG3
#define ISSI_REG_CSPULLUP 0x10 // PG3
#ifndef ISSI_TIMEOUT
# define ISSI_TIMEOUT 100
#ifndef IS31FL3733_I2C_TIMEOUT
# define IS31FL3733_I2C_TIMEOUT 100
#endif
#ifndef ISSI_PERSISTENCE
# define ISSI_PERSISTENCE 0
#ifndef IS31FL3733_I2C_PERSISTENCE
# define IS31FL3733_I2C_PERSISTENCE 0
#endif
#ifndef ISSI_PWM_FREQUENCY
# define ISSI_PWM_FREQUENCY 0b000 // PFS - IS31FL3733B only
#ifndef IS31FL3733_PWM_FREQUENCY
# define IS31FL3733_PWM_FREQUENCY IS31FL3733_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3733B only
#endif
#ifndef ISSI_SWPULLUP
# define ISSI_SWPULLUP PUR_0R
#ifndef IS31FL3733_SWPULLUP
# define IS31FL3733_SWPULLUP IS31FL3733_PUR_0R
#endif
#ifndef ISSI_CSPULLUP
# define ISSI_CSPULLUP PUR_0R
#ifndef IS31FL3733_CSPULLUP
# define IS31FL3733_CSPULLUP IS31FL3733_PUR_0R
#endif
#ifndef ISSI_GLOBALCURRENT
# define ISSI_GLOBALCURRENT 0xFF
#ifndef IS31FL3733_GLOBALCURRENT
# define IS31FL3733_GLOBALCURRENT 0xFF
#endif
// Transfer buffer for TWITransmitData()
@ -83,34 +72,25 @@ uint8_t g_twi_transfer_buffer[20];
// We could optimize this and take out the unused registers from these
// 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};
uint8_t g_pwm_buffer[IS31FL3733_DRIVER_COUNT][192];
bool g_pwm_buffer_update_required[IS31FL3733_DRIVER_COUNT] = {false};
/* There's probably a better way to init this... */
#if LED_DRIVER_COUNT == 1
uint8_t g_led_control_registers[LED_DRIVER_COUNT][24] = {{0}};
#elif LED_DRIVER_COUNT == 2
uint8_t g_led_control_registers[LED_DRIVER_COUNT][24] = {{0}, {0}};
#elif LED_DRIVER_COUNT == 3
uint8_t g_led_control_registers[LED_DRIVER_COUNT][24] = {{0}, {0}, {0}};
#elif LED_DRIVER_COUNT == 4
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};
uint8_t g_led_control_registers[IS31FL3733_DRIVER_COUNT][24] = {0};
bool g_led_control_registers_update_required[IS31FL3733_DRIVER_COUNT] = {false};
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;
#if ISSI_PERSISTENCE > 0
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) != 0) {
#if IS31FL3733_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3733_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3733_I2C_TIMEOUT) != 0) {
return false;
}
}
#else
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) != 0) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3733_I2C_TIMEOUT) != 0) {
return false;
}
#endif
@ -131,14 +111,14 @@ bool is31fl3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
// Thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer.
memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
#if ISSI_PERSISTENCE > 0
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) != 0) {
#if IS31FL3733_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3733_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3733_I2C_TIMEOUT) != 0) {
return false;
}
}
#else
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) != 0) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3733_I2C_TIMEOUT) != 0) {
return false;
}
#endif
@ -154,20 +134,20 @@ 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, IS31FL3733_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG0
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
is31fl3733_write_register(addr, IS31FL3733_COMMANDREGISTER, IS31FL3733_PAGE_LEDCONTROL);
// Turn off all LEDs.
for (int i = 0x00; i <= 0x17; i++) {
is31fl3733_write_register(addr, i, 0x00);
}
// Unlock the command register.
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3733_write_register(addr, IS31FL3733_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG1
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
is31fl3733_write_register(addr, IS31FL3733_COMMANDREGISTER, IS31FL3733_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++) {
@ -175,27 +155,27 @@ void is31fl3733_init(uint8_t addr, uint8_t sync) {
}
// Unlock the command register.
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3733_write_register(addr, IS31FL3733_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG3
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
is31fl3733_write_register(addr, IS31FL3733_COMMANDREGISTER, IS31FL3733_PAGE_FUNCTION);
// Set de-ghost pull-up resistors (SWx)
is31fl3733_write_register(addr, ISSI_REG_SWPULLUP, ISSI_SWPULLUP);
is31fl3733_write_register(addr, IS31FL3733_REG_SWPULLUP, IS31FL3733_SWPULLUP);
// Set de-ghost pull-down resistors (CSx)
is31fl3733_write_register(addr, ISSI_REG_CSPULLUP, ISSI_CSPULLUP);
is31fl3733_write_register(addr, IS31FL3733_REG_CSPULLUP, IS31FL3733_CSPULLUP);
// Set global current to maximum.
is31fl3733_write_register(addr, ISSI_REG_GLOBALCURRENT, ISSI_GLOBALCURRENT);
is31fl3733_write_register(addr, IS31FL3733_REG_GLOBALCURRENT, IS31FL3733_GLOBALCURRENT);
// Disable software shutdown.
is31fl3733_write_register(addr, ISSI_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((ISSI_PWM_FREQUENCY & 0b111) << 3) | 0x01);
is31fl3733_write_register(addr, IS31FL3733_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((IS31FL3733_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) {
is31_led led;
is31fl3733_led_t led;
if (index >= 0 && index < LED_MATRIX_LED_COUNT) {
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led));
if (g_pwm_buffer[led.driver][led.v] == value) {
return;
@ -212,8 +192,8 @@ void is31fl3733_set_value_all(uint8_t value) {
}
void is31fl3733_set_led_control_register(uint8_t index, bool value) {
is31_led led;
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
is31fl3733_led_t led;
memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led));
uint8_t control_register = led.v / 8;
uint8_t bit_value = led.v % 8;
@ -230,8 +210,8 @@ void is31fl3733_set_led_control_register(uint8_t index, bool value) {
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, IS31FL3733_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3733_write_register(addr, IS31FL3733_COMMANDREGISTER, IS31FL3733_PAGE_PWM);
// If any of the transactions fail we risk writing dirty PG0,
// refresh page 0 just in case.
@ -245,8 +225,8 @@ void is31fl3733_update_pwm_buffers(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, IS31FL3733_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3733_write_register(addr, IS31FL3733_COMMANDREGISTER, IS31FL3733_PAGE_LEDCONTROL);
for (int i = 0; i < 24; i++) {
is31fl3733_write_register(addr, i, g_led_control_registers[index][i]);
}

View File

@ -22,15 +22,66 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "progmem.h"
typedef struct is31_led {
// ======== DEPRECATED DEFINES - DO NOT USE ========
#ifdef LED_DRIVER_COUNT
# define IS31FL3733_DRIVER_COUNT LED_DRIVER_COUNT
#endif
#ifdef ISSI_TIMEOUT
# define IS31FL3733_I2C_TIMEOUT ISSI_TIMEOUT
#endif
#ifdef ISSI_PERSISTENCE
# define IS31FL3733_I2C_PERSISTENCE ISSI_PERSISTENCE
#endif
#ifdef ISSI_PWM_FREQUENCY
# define IS31FL3733_PWM_FREQUENCY ISSI_PWM_FREQUENCY
#endif
#ifdef ISSI_SWPULLUP
# define IS31FL3733_SWPULLUP ISSI_SWPULLUP
#endif
#ifdef ISSI_CSPULLUP
# define IS31FL3733_CSPULLUP ISSI_CSPULLUP
#endif
#ifdef ISSI_GLOBALCURRENT
# define IS31FL3733_GLOBALCURRENT ISSI_GLOBALCURRENT
#endif
#define is31_led is31fl3733_led_t
#define g_is31_leds g_is31fl3733_leds
#define PUR_0R IS31FL3733_PUR_0R
#define PUR_05KR IS31FL3733_PUR_05KR
#define PUR_3KR IS31FL3733_PUR_3KR
#define PUR_4KR IS31FL3733_PUR_4KR
#define PUR_8KR IS31FL3733_PUR_8KR
#define PUR_16KR IS31FL3733_PUR_16KR
#define PUR_32KR IS31FL3733_PUR_32KR
// ========
#define IS31FL3733_I2C_ADDRESS_GND_GND 0x50
#define IS31FL3733_I2C_ADDRESS_GND_SCL 0x51
#define IS31FL3733_I2C_ADDRESS_GND_SDA 0x52
#define IS31FL3733_I2C_ADDRESS_GND_VCC 0x53
#define IS31FL3733_I2C_ADDRESS_SCL_GND 0x54
#define IS31FL3733_I2C_ADDRESS_SCL_SCL 0x55
#define IS31FL3733_I2C_ADDRESS_SCL_SDA 0x56
#define IS31FL3733_I2C_ADDRESS_SCL_VCC 0x57
#define IS31FL3733_I2C_ADDRESS_SDA_GND 0x58
#define IS31FL3733_I2C_ADDRESS_SDA_SCL 0x59
#define IS31FL3733_I2C_ADDRESS_SDA_SDA 0x5A
#define IS31FL3733_I2C_ADDRESS_SDA_VCC 0x5B
#define IS31FL3733_I2C_ADDRESS_VCC_GND 0x5C
#define IS31FL3733_I2C_ADDRESS_VCC_SCL 0x5D
#define IS31FL3733_I2C_ADDRESS_VCC_SDA 0x5E
#define IS31FL3733_I2C_ADDRESS_VCC_VCC 0x5F
typedef struct is31fl3733_led_t {
uint8_t driver : 2;
uint8_t v;
} __attribute__((packed)) is31_led;
} __attribute__((packed)) is31fl3733_led_t;
extern const is31_led PROGMEM g_is31_leds[LED_MATRIX_LED_COUNT];
extern const is31fl3733_led_t PROGMEM g_is31fl3733_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);
@ -48,13 +99,19 @@ void is31fl3733_set_led_control_register(uint8_t index, bool value);
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
#define PUR_3KR 0x03 // 3.0k Ohm resistor on all the time
#define PUR_4KR 0x04 // 4.0k Ohm resistor on all the time
#define PUR_8KR 0x05 // 8.0k Ohm resistor on all the time
#define PUR_16KR 0x06 // 16k Ohm resistor on all the time
#define PUR_32KR 0x07 // 32k Ohm resistor in t_NOL
#define IS31FL3733_PUR_0R 0x00 // No PUR resistor
#define IS31FL3733_PUR_05KR 0x02 // 0.5k Ohm resistor in t_NOL
#define IS31FL3733_PUR_3KR 0x03 // 3.0k Ohm resistor on all the time
#define IS31FL3733_PUR_4KR 0x04 // 4.0k Ohm resistor on all the time
#define IS31FL3733_PUR_8KR 0x05 // 8.0k Ohm resistor on all the time
#define IS31FL3733_PUR_16KR 0x06 // 16k Ohm resistor on all the time
#define IS31FL3733_PUR_32KR 0x07 // 32k Ohm resistor in t_NOL
#define IS31FL3733_PWM_FREQUENCY_8K4_HZ 0b000
#define IS31FL3733_PWM_FREQUENCY_4K2_HZ 0b001
#define IS31FL3733_PWM_FREQUENCY_26K7_HZ 0b010
#define IS31FL3733_PWM_FREQUENCY_2K1_HZ 0b011
#define IS31FL3733_PWM_FREQUENCY_1K05_HZ 0b100
#define A_1 0x00
#define A_2 0x01

View File

@ -18,59 +18,48 @@
*/
#include "is31fl3733.h"
#include <string.h>
#include "i2c_master.h"
#include "wait.h"
// This is a 7-bit address, that gets left-shifted and bit 0
// set to 0 for write, 1 for read (as per I2C protocol)
// The address will vary depending on your wiring:
// 00 <-> GND
// 01 <-> SCL
// 10 <-> SDA
// 11 <-> VCC
// ADDR1 represents A1:A0 of the 7-bit address.
// ADDR2 represents A3:A2 of the 7-bit address.
// The result is: 0b101(ADDR2)(ADDR1)
#define ISSI_ADDR_DEFAULT 0x50
#define IS31FL3733_COMMANDREGISTER 0xFD
#define IS31FL3733_COMMANDREGISTER_WRITELOCK 0xFE
#define IS31FL3733_INTERRUPTMASKREGISTER 0xF0
#define IS31FL3733_INTERRUPTSTATUSREGISTER 0xF1
#define ISSI_COMMANDREGISTER 0xFD
#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE
#define ISSI_INTERRUPTMASKREGISTER 0xF0
#define ISSI_INTERRUPTSTATUSREGISTER 0xF1
#define IS31FL3733_PAGE_LEDCONTROL 0x00 // PG0
#define IS31FL3733_PAGE_PWM 0x01 // PG1
#define IS31FL3733_PAGE_AUTOBREATH 0x02 // PG2
#define IS31FL3733_PAGE_FUNCTION 0x03 // PG3
#define ISSI_PAGE_LEDCONTROL 0x00 // PG0
#define ISSI_PAGE_PWM 0x01 // PG1
#define ISSI_PAGE_AUTOBREATH 0x02 // PG2
#define ISSI_PAGE_FUNCTION 0x03 // PG3
#define IS31FL3733_REG_CONFIGURATION 0x00 // PG3
#define IS31FL3733_REG_GLOBALCURRENT 0x01 // PG3
#define IS31FL3733_REG_RESET 0x11 // PG3
#define IS31FL3733_REG_SWPULLUP 0x0F // PG3
#define IS31FL3733_REG_CSPULLUP 0x10 // PG3
#define ISSI_REG_CONFIGURATION 0x00 // PG3
#define ISSI_REG_GLOBALCURRENT 0x01 // PG3
#define ISSI_REG_RESET 0x11 // PG3
#define ISSI_REG_SWPULLUP 0x0F // PG3
#define ISSI_REG_CSPULLUP 0x10 // PG3
#ifndef ISSI_TIMEOUT
# define ISSI_TIMEOUT 100
#ifndef IS31FL3733_I2C_TIMEOUT
# define IS31FL3733_I2C_TIMEOUT 100
#endif
#ifndef ISSI_PERSISTENCE
# define ISSI_PERSISTENCE 0
#ifndef IS31FL3733_I2C_PERSISTENCE
# define IS31FL3733_I2C_PERSISTENCE 0
#endif
#ifndef ISSI_PWM_FREQUENCY
# define ISSI_PWM_FREQUENCY 0b000 // PFS - IS31FL3733B only
#ifndef IS31FL3733_PWM_FREQUENCY
# define IS31FL3733_PWM_FREQUENCY IS31FL3733_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3733B only
#endif
#ifndef ISSI_SWPULLUP
# define ISSI_SWPULLUP PUR_0R
#ifndef IS31FL3733_SWPULLUP
# define IS31FL3733_SWPULLUP IS31FL3733_PUR_0R
#endif
#ifndef ISSI_CSPULLUP
# define ISSI_CSPULLUP PUR_0R
#ifndef IS31FL3733_CSPULLUP
# define IS31FL3733_CSPULLUP IS31FL3733_PUR_0R
#endif
#ifndef ISSI_GLOBALCURRENT
# define ISSI_GLOBALCURRENT 0xFF
#ifndef IS31FL3733_GLOBALCURRENT
# define IS31FL3733_GLOBALCURRENT 0xFF
#endif
// Transfer buffer for TWITransmitData()
@ -82,25 +71,25 @@ uint8_t g_twi_transfer_buffer[20];
// We could optimize this and take out the unused registers from these
// 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};
uint8_t g_pwm_buffer[IS31FL3733_DRIVER_COUNT][192];
bool g_pwm_buffer_update_required[IS31FL3733_DRIVER_COUNT] = {false};
uint8_t g_led_control_registers[DRIVER_COUNT][24] = {0};
bool g_led_control_registers_update_required[DRIVER_COUNT] = {false};
uint8_t g_led_control_registers[IS31FL3733_DRIVER_COUNT][24] = {0};
bool g_led_control_registers_update_required[IS31FL3733_DRIVER_COUNT] = {false};
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;
#if ISSI_PERSISTENCE > 0
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) != 0) {
#if IS31FL3733_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3733_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3733_I2C_TIMEOUT) != 0) {
return false;
}
}
#else
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) != 0) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3733_I2C_TIMEOUT) != 0) {
return false;
}
#endif
@ -119,18 +108,16 @@ bool is31fl3733_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
// Copy the data from i to i+15.
// Device will auto-increment register for data after the first byte
// Thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer.
for (int j = 0; j < 16; j++) {
g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
}
memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
#if ISSI_PERSISTENCE > 0
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) != 0) {
#if IS31FL3733_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3733_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3733_I2C_TIMEOUT) != 0) {
return false;
}
}
#else
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) != 0) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3733_I2C_TIMEOUT) != 0) {
return false;
}
#endif
@ -146,20 +133,20 @@ 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, IS31FL3733_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG0
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
is31fl3733_write_register(addr, IS31FL3733_COMMANDREGISTER, IS31FL3733_PAGE_LEDCONTROL);
// Turn off all LEDs.
for (int i = 0x00; i <= 0x17; i++) {
is31fl3733_write_register(addr, i, 0x00);
}
// Unlock the command register.
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3733_write_register(addr, IS31FL3733_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG1
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
is31fl3733_write_register(addr, IS31FL3733_COMMANDREGISTER, IS31FL3733_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++) {
@ -167,27 +154,27 @@ void is31fl3733_init(uint8_t addr, uint8_t sync) {
}
// Unlock the command register.
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3733_write_register(addr, IS31FL3733_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG3
is31fl3733_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
is31fl3733_write_register(addr, IS31FL3733_COMMANDREGISTER, IS31FL3733_PAGE_FUNCTION);
// Set de-ghost pull-up resistors (SWx)
is31fl3733_write_register(addr, ISSI_REG_SWPULLUP, ISSI_SWPULLUP);
is31fl3733_write_register(addr, IS31FL3733_REG_SWPULLUP, IS31FL3733_SWPULLUP);
// Set de-ghost pull-down resistors (CSx)
is31fl3733_write_register(addr, ISSI_REG_CSPULLUP, ISSI_CSPULLUP);
is31fl3733_write_register(addr, IS31FL3733_REG_CSPULLUP, IS31FL3733_CSPULLUP);
// Set global current to maximum.
is31fl3733_write_register(addr, ISSI_REG_GLOBALCURRENT, ISSI_GLOBALCURRENT);
is31fl3733_write_register(addr, IS31FL3733_REG_GLOBALCURRENT, IS31FL3733_GLOBALCURRENT);
// Disable software shutdown.
is31fl3733_write_register(addr, ISSI_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((ISSI_PWM_FREQUENCY & 0b111) << 3) | 0x01);
is31fl3733_write_register(addr, IS31FL3733_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((IS31FL3733_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) {
is31_led led;
is31fl3733_led_t led;
if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
memcpy_P(&led, (&g_is31fl3733_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;
@ -206,8 +193,8 @@ 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) {
is31_led led;
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
is31fl3733_led_t led;
memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led));
uint8_t control_register_r = led.r / 8;
uint8_t control_register_g = led.g / 8;
@ -238,26 +225,26 @@ void is31fl3733_set_led_control_register(uint8_t index, bool red, bool green, bo
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, IS31FL3733_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3733_write_register(addr, IS31FL3733_COMMANDREGISTER, IS31FL3733_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])) {
g_led_control_registers_update_required[index] = true;
}
g_pwm_buffer_update_required[index] = false;
}
g_pwm_buffer_update_required[index] = false;
}
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, IS31FL3733_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3733_write_register(addr, IS31FL3733_COMMANDREGISTER, IS31FL3733_PAGE_LEDCONTROL);
for (int i = 0; i < 24; i++) {
is31fl3733_write_register(addr, i, g_led_control_registers[index][i]);
}
g_led_control_registers_update_required[index] = false;
}
g_led_control_registers_update_required[index] = false;
}

View File

@ -23,14 +23,66 @@
#include <stdbool.h>
#include "progmem.h"
typedef struct is31_led {
// ======== DEPRECATED DEFINES - DO NOT USE ========
#ifdef DRIVER_COUNT
# define IS31FL3733_DRIVER_COUNT DRIVER_COUNT
#endif
#ifdef ISSI_TIMEOUT
# define IS31FL3733_I2C_TIMEOUT ISSI_TIMEOUT
#endif
#ifdef ISSI_PERSISTENCE
# define IS31FL3733_I2C_PERSISTENCE ISSI_PERSISTENCE
#endif
#ifdef ISSI_PWM_FREQUENCY
# define IS31FL3733_PWM_FREQUENCY ISSI_PWM_FREQUENCY
#endif
#ifdef ISSI_SWPULLUP
# define IS31FL3733_SWPULLUP ISSI_SWPULLUP
#endif
#ifdef ISSI_CSPULLUP
# define IS31FL3733_CSPULLUP ISSI_CSPULLUP
#endif
#ifdef ISSI_GLOBALCURRENT
# define IS31FL3733_GLOBALCURRENT ISSI_GLOBALCURRENT
#endif
#define is31_led is31fl3733_led_t
#define g_is31_leds g_is31fl3733_leds
#define PUR_0R IS31FL3733_PUR_0R
#define PUR_05KR IS31FL3733_PUR_05KR
#define PUR_3KR IS31FL3733_PUR_3KR
#define PUR_4KR IS31FL3733_PUR_4KR
#define PUR_8KR IS31FL3733_PUR_8KR
#define PUR_16KR IS31FL3733_PUR_16KR
#define PUR_32KR IS31FL3733_PUR_32KR
// ========
#define IS31FL3733_I2C_ADDRESS_GND_GND 0x50
#define IS31FL3733_I2C_ADDRESS_GND_SCL 0x51
#define IS31FL3733_I2C_ADDRESS_GND_SDA 0x52
#define IS31FL3733_I2C_ADDRESS_GND_VCC 0x53
#define IS31FL3733_I2C_ADDRESS_SCL_GND 0x54
#define IS31FL3733_I2C_ADDRESS_SCL_SCL 0x55
#define IS31FL3733_I2C_ADDRESS_SCL_SDA 0x56
#define IS31FL3733_I2C_ADDRESS_SCL_VCC 0x57
#define IS31FL3733_I2C_ADDRESS_SDA_GND 0x58
#define IS31FL3733_I2C_ADDRESS_SDA_SCL 0x59
#define IS31FL3733_I2C_ADDRESS_SDA_SDA 0x5A
#define IS31FL3733_I2C_ADDRESS_SDA_VCC 0x5B
#define IS31FL3733_I2C_ADDRESS_VCC_GND 0x5C
#define IS31FL3733_I2C_ADDRESS_VCC_SCL 0x5D
#define IS31FL3733_I2C_ADDRESS_VCC_SDA 0x5E
#define IS31FL3733_I2C_ADDRESS_VCC_VCC 0x5F
typedef struct is31fl3733_led_t {
uint8_t driver : 2;
uint8_t r;
uint8_t g;
uint8_t b;
} __attribute__((packed)) is31_led;
} __attribute__((packed)) is31fl3733_led_t;
extern const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT];
extern const is31fl3733_led_t PROGMEM g_is31fl3733_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);
@ -48,13 +100,19 @@ void is31fl3733_set_led_control_register(uint8_t index, bool red, bool green, bo
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
#define PUR_3KR 0x03 // 3.0k Ohm resistor on all the time
#define PUR_4KR 0x04 // 4.0k Ohm resistor on all the time
#define PUR_8KR 0x05 // 8.0k Ohm resistor on all the time
#define PUR_16KR 0x06 // 16k Ohm resistor on all the time
#define PUR_32KR 0x07 // 32k Ohm resistor in t_NOL
#define IS31FL3733_PUR_0R 0x00 // No PUR resistor
#define IS31FL3733_PUR_05KR 0x02 // 0.5k Ohm resistor in t_NOL
#define IS31FL3733_PUR_3KR 0x03 // 3.0k Ohm resistor on all the time
#define IS31FL3733_PUR_4KR 0x04 // 4.0k Ohm resistor on all the time
#define IS31FL3733_PUR_8KR 0x05 // 8.0k Ohm resistor on all the time
#define IS31FL3733_PUR_16KR 0x06 // 16k Ohm resistor on all the time
#define IS31FL3733_PUR_32KR 0x07 // 32k Ohm resistor in t_NOL
#define IS31FL3733_PWM_FREQUENCY_8K4_HZ 0b000
#define IS31FL3733_PWM_FREQUENCY_4K2_HZ 0b001
#define IS31FL3733_PWM_FREQUENCY_26K7_HZ 0b010
#define IS31FL3733_PWM_FREQUENCY_2K1_HZ 0b011
#define IS31FL3733_PWM_FREQUENCY_1K05_HZ 0b100
#define A_1 0x00
#define A_2 0x01

View File

@ -0,0 +1,222 @@
/* Copyright 2018 Jason Williams (Wilba)
* Copyright 2021 Doni Crosby
*
* 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 "is31fl3736-simple.h"
#include <string.h>
#include "i2c_master.h"
#include "wait.h"
#define IS31FL3736_COMMANDREGISTER 0xFD
#define IS31FL3736_COMMANDREGISTER_WRITELOCK 0xFE
#define IS31FL3736_INTERRUPTMASKREGISTER 0xF0
#define IS31FL3736_INTERRUPTSTATUSREGISTER 0xF1
#define IS31FL3736_PAGE_LEDCONTROL 0x00 // PG0
#define IS31FL3736_PAGE_PWM 0x01 // PG1
#define IS31FL3736_PAGE_AUTOBREATH 0x02 // PG2
#define IS31FL3736_PAGE_FUNCTION 0x03 // PG3
#define IS31FL3736_REG_CONFIGURATION 0x00 // PG3
#define IS31FL3736_REG_GLOBALCURRENT 0x01 // PG3
#define IS31FL3736_REG_RESET 0x11 // PG3
#define IS31FL3736_REG_SWPULLUP 0x0F // PG3
#define IS31FL3736_REG_CSPULLUP 0x10 // PG3
#ifndef IS31FL3736_I2C_TIMEOUT
# define IS31FL3736_I2C_TIMEOUT 100
#endif
#ifndef IS31FL3736_I2C_PERSISTENCE
# define IS31FL3736_I2C_PERSISTENCE 0
#endif
#ifndef IS31FL3736_PWM_FREQUENCY
# define IS31FL3736_PWM_FREQUENCY IS31FL3736_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3736B only
#endif
#ifndef IS31FL3736_SWPULLUP
# define IS31FL3736_SWPULLUP IS31FL3736_PUR_0R
#endif
#ifndef IS31FL3736_CSPULLUP
# define IS31FL3736_CSPULLUP IS31FL3736_PUR_0R
#endif
#ifndef IS31FL3736_GLOBALCURRENT
# define IS31FL3736_GLOBALCURRENT 0xFF
#endif
// Transfer buffer for TWITransmitData()
uint8_t g_twi_transfer_buffer[20];
// These buffers match the IS31FL3736 PWM registers.
// 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
// probably not worth the extra complexity.
uint8_t g_pwm_buffer[IS31FL3736_DRIVER_COUNT][192];
bool g_pwm_buffer_update_required[IS31FL3736_DRIVER_COUNT] = {false};
uint8_t g_led_control_registers[IS31FL3736_DRIVER_COUNT][24] = {0};
bool g_led_control_registers_update_required[IS31FL3736_DRIVER_COUNT] = {false};
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;
#if IS31FL3736_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3736_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3736_I2C_TIMEOUT) == 0) break;
}
#else
i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3736_I2C_TIMEOUT);
#endif
}
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
// g_twi_transfer_buffer[] is 20 bytes
// iterate over the pwm_buffer contents at 16 byte intervals
for (int i = 0; i < 192; i += 16) {
g_twi_transfer_buffer[0] = i;
// copy the data from i to i+15
// device will auto-increment register for data after the first byte
// thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer
memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
#if IS31FL3736_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3736_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3736_I2C_TIMEOUT) == 0) break;
}
#else
i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3736_I2C_TIMEOUT);
#endif
}
}
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, IS31FL3736_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG0
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER, IS31FL3736_PAGE_LEDCONTROL);
// Turn off all LEDs.
for (int i = 0x00; i <= 0x17; i++) {
is31fl3736_write_register(addr, i, 0x00);
}
// Unlock the command register.
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG1
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER, IS31FL3736_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);
}
// Unlock the command register.
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG3
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER, IS31FL3736_PAGE_FUNCTION);
// Set de-ghost pull-up resistors (SWx)
is31fl3736_write_register(addr, IS31FL3736_REG_SWPULLUP, IS31FL3736_SWPULLUP);
// Set de-ghost pull-down resistors (CSx)
is31fl3736_write_register(addr, IS31FL3736_REG_CSPULLUP, IS31FL3736_CSPULLUP);
// Set global current to maximum.
is31fl3736_write_register(addr, IS31FL3736_REG_GLOBALCURRENT, IS31FL3736_GLOBALCURRENT);
// Disable software shutdown.
is31fl3736_write_register(addr, IS31FL3736_REG_CONFIGURATION, ((IS31FL3736_PWM_FREQUENCY & 0b111) << 3) | 0x01);
// Wait 10ms to ensure the device has woken up.
wait_ms(10);
}
void is31fl3736_set_value(int index, uint8_t value) {
is31fl3736_led_t led;
if (index >= 0 && index < LED_MATRIX_LED_COUNT) {
memcpy_P(&led, (&g_is31fl3736_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 is31fl3736_set_value_all(uint8_t value) {
for (int i = 0; i < LED_MATRIX_LED_COUNT; i++) {
is31fl3736_set_value(i, value);
}
}
void is31fl3736_set_led_control_register(uint8_t index, bool value) {
is31fl3736_led_t led;
memcpy_P(&led, (&g_is31fl3736_leds[index]), sizeof(led));
// The PWM register for a matrix position (0x00 to 0xBF) is interleaved, so:
// A1=0x00 A2=0x02 A3=0x04 A4=0x06 A5=0x08 A6=0x0A A7=0x0C A8=0x0E
// B1=0x10 B2=0x12 B3=0x14
// But also, the LED control registers (0x00 to 0x17) are also interleaved, so:
// A1-A4=0x00 A5-A8=0x01
uint8_t control_register = led.v / 8;
uint8_t bit_value = led.v % 8;
if (value) {
g_led_control_registers[led.driver][control_register] |= (1 << bit_value);
} else {
g_led_control_registers[led.driver][control_register] &= ~(1 << bit_value);
}
g_led_control_registers_update_required[led.driver] = true;
}
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, IS31FL3736_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER, IS31FL3736_PAGE_PWM);
is31fl3736_write_pwm_buffer(addr, g_pwm_buffer[index]);
g_pwm_buffer_update_required[index] = false;
}
}
void is31fl3736_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
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER, IS31FL3736_PAGE_LEDCONTROL);
for (int i = 0; i < 24; i++) {
is31fl3736_write_register(addr, i, g_led_control_registers[index][i]);
}
g_led_control_registers_update_required[index] = false;
}
}

View File

@ -0,0 +1,218 @@
/* Copyright 2018 Jason Williams (Wilba)
* Copyright 2021 Doni Crosby
*
* 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>
#include <stdbool.h>
#include "progmem.h"
// ======== DEPRECATED DEFINES - DO NOT USE ========
#ifdef DRIVER_COUNT
# define IS31FL3736_DRIVER_COUNT DRIVER_COUNT
#endif
#ifdef ISSI_TIMEOUT
# define IS31FL3736_I2C_TIMEOUT ISSI_TIMEOUT
#endif
#ifdef ISSI_PERSISTENCE
# define IS31FL3736_I2C_PERSISTENCE ISSI_PERSISTENCE
#endif
#ifdef ISSI_SWPULLUP
# define IS31FL3736_SWPULLUP ISSI_SWPULLUP
#endif
#ifdef ISSI_CSPULLUP
# define IS31FL3736_CSPULLUP ISSI_CSPULLUP
#endif
#ifdef ISSI_GLOBALCURRENT
# define IS31FL3736_GLOBALCURRENT ISSI_GLOBALCURRENT
#endif
#define is31_led is31fl3736_led_t
#define g_is31_leds g_is31fl3736_leds
#define PUR_0R IS31FL3736_PUR_0R
#define PUR_05KR IS31FL3736_PUR_05KR
#define PUR_1KR IS31FL3736_PUR_1KR
#define PUR_2KR IS31FL3736_PUR_2KR
#define PUR_4KR IS31FL3736_PUR_4KR
#define PUR_8KR IS31FL3736_PUR_8KR
#define PUR_16KR IS31FL3736_PUR_16KR
#define PUR_32KR IS31FL3736_PUR_32KR
// ========
#define IS31FL3736_I2C_ADDRESS_GND_GND 0x50
#define IS31FL3736_I2C_ADDRESS_GND_SCL 0x51
#define IS31FL3736_I2C_ADDRESS_GND_SDA 0x52
#define IS31FL3736_I2C_ADDRESS_GND_VCC 0x53
#define IS31FL3736_I2C_ADDRESS_SCL_GND 0x54
#define IS31FL3736_I2C_ADDRESS_SCL_SCL 0x55
#define IS31FL3736_I2C_ADDRESS_SCL_SDA 0x56
#define IS31FL3736_I2C_ADDRESS_SCL_VCC 0x57
#define IS31FL3736_I2C_ADDRESS_SDA_GND 0x58
#define IS31FL3736_I2C_ADDRESS_SDA_SCL 0x59
#define IS31FL3736_I2C_ADDRESS_SDA_SDA 0x5A
#define IS31FL3736_I2C_ADDRESS_SDA_VCC 0x5B
#define IS31FL3736_I2C_ADDRESS_VCC_GND 0x5C
#define IS31FL3736_I2C_ADDRESS_VCC_SCL 0x5D
#define IS31FL3736_I2C_ADDRESS_VCC_SDA 0x5E
#define IS31FL3736_I2C_ADDRESS_VCC_VCC 0x5F
typedef struct is31fl3736_led_t {
uint8_t driver : 2;
uint8_t v;
} __attribute__((packed)) is31fl3736_led_t;
extern const is31fl3736_led_t PROGMEM g_is31fl3736_leds[LED_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_set_value(int index, uint8_t value);
void is31fl3736_set_value_all(uint8_t value);
void is31fl3736_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 is31fl3736_update_pwm_buffers(uint8_t addr, uint8_t index);
void is31fl3736_update_led_control_registers(uint8_t addr, uint8_t index);
#define IS31FL3736_PUR_0R 0x00 // No PUR resistor
#define IS31FL3736_PUR_05KR 0x01 // 0.5k Ohm resistor
#define IS31FL3736_PUR_1KR 0x02 // 1.0k Ohm resistor
#define IS31FL3736_PUR_2KR 0x03 // 2.0k Ohm resistor
#define IS31FL3736_PUR_4KR 0x04 // 4.0k Ohm resistor
#define IS31FL3736_PUR_8KR 0x05 // 8.0k Ohm resistor
#define IS31FL3736_PUR_16KR 0x06 // 16k Ohm resistor
#define IS31FL3736_PUR_32KR 0x07 // 32k Ohm resistor
#define IS31FL3736_PWM_FREQUENCY_8K4_HZ 0b000
#define IS31FL3736_PWM_FREQUENCY_4K2_HZ 0b001
#define IS31FL3736_PWM_FREQUENCY_26K7_HZ 0b010
#define IS31FL3736_PWM_FREQUENCY_2K1_HZ 0b011
#define IS31FL3736_PWM_FREQUENCY_1K05_HZ 0b100
#define A_1 0x00
#define A_2 0x02
#define A_3 0x04
#define A_4 0x06
#define A_5 0x08
#define A_6 0x0A
#define A_7 0x0C
#define A_8 0x0E
#define B_1 0x10
#define B_2 0x12
#define B_3 0x14
#define B_4 0x16
#define B_5 0x18
#define B_6 0x1A
#define B_7 0x1C
#define B_8 0x1E
#define C_1 0x20
#define C_2 0x22
#define C_3 0x24
#define C_4 0x26
#define C_5 0x28
#define C_6 0x2A
#define C_7 0x2C
#define C_8 0x2E
#define D_1 0x30
#define D_2 0x32
#define D_3 0x34
#define D_4 0x36
#define D_5 0x38
#define D_6 0x3A
#define D_7 0x3C
#define D_8 0x3E
#define E_1 0x40
#define E_2 0x42
#define E_3 0x44
#define E_4 0x46
#define E_5 0x48
#define E_6 0x4A
#define E_7 0x4C
#define E_8 0x4E
#define F_1 0x50
#define F_2 0x52
#define F_3 0x54
#define F_4 0x56
#define F_5 0x58
#define F_6 0x5A
#define F_7 0x5C
#define F_8 0x5E
#define G_1 0x60
#define G_2 0x62
#define G_3 0x64
#define G_4 0x66
#define G_5 0x68
#define G_6 0x6A
#define G_7 0x6C
#define G_8 0x6E
#define H_1 0x70
#define H_2 0x72
#define H_3 0x74
#define H_4 0x76
#define H_5 0x78
#define H_6 0x7A
#define H_7 0x7C
#define H_8 0x7E
#define I_1 0x80
#define I_2 0x82
#define I_3 0x84
#define I_4 0x86
#define I_5 0x88
#define I_6 0x8A
#define I_7 0x8C
#define I_8 0x8E
#define J_1 0x90
#define J_2 0x92
#define J_3 0x94
#define J_4 0x96
#define J_5 0x98
#define J_6 0x9A
#define J_7 0x9C
#define J_8 0x9E
#define K_1 0xA0
#define K_2 0xA2
#define K_3 0xA4
#define K_4 0xA6
#define K_5 0xA8
#define K_6 0xAA
#define K_7 0xAC
#define K_8 0xAE
#define L_1 0xB0
#define L_2 0xB2
#define L_3 0xB4
#define L_4 0xB6
#define L_5 0xB8
#define L_6 0xBA
#define L_7 0xBC
#define L_8 0xBE

View File

@ -16,55 +16,48 @@
*/
#include "is31fl3736.h"
#include <string.h>
#include "i2c_master.h"
#include "wait.h"
// This is a 7-bit address, that gets left-shifted and bit 0
// set to 0 for write, 1 for read (as per I2C protocol)
// The address will vary depending on your wiring:
// 00 <-> GND
// 01 <-> SCL
// 10 <-> SDA
// 11 <-> VCC
// ADDR1 represents A1:A0 of the 7-bit address.
// ADDR2 represents A3:A2 of the 7-bit address.
// The result is: 0b101(ADDR2)(ADDR1)
#define ISSI_ADDR_DEFAULT 0x50
#define IS31FL3736_COMMANDREGISTER 0xFD
#define IS31FL3736_COMMANDREGISTER_WRITELOCK 0xFE
#define IS31FL3736_INTERRUPTMASKREGISTER 0xF0
#define IS31FL3736_INTERRUPTSTATUSREGISTER 0xF1
#define ISSI_COMMANDREGISTER 0xFD
#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE
#define ISSI_INTERRUPTMASKREGISTER 0xF0
#define ISSI_INTERRUPTSTATUSREGISTER 0xF1
#define IS31FL3736_PAGE_LEDCONTROL 0x00 // PG0
#define IS31FL3736_PAGE_PWM 0x01 // PG1
#define IS31FL3736_PAGE_AUTOBREATH 0x02 // PG2
#define IS31FL3736_PAGE_FUNCTION 0x03 // PG3
#define ISSI_PAGE_LEDCONTROL 0x00 // PG0
#define ISSI_PAGE_PWM 0x01 // PG1
#define ISSI_PAGE_AUTOBREATH 0x02 // PG2
#define ISSI_PAGE_FUNCTION 0x03 // PG3
#define IS31FL3736_REG_CONFIGURATION 0x00 // PG3
#define IS31FL3736_REG_GLOBALCURRENT 0x01 // PG3
#define IS31FL3736_REG_RESET 0x11 // PG3
#define IS31FL3736_REG_SWPULLUP 0x0F // PG3
#define IS31FL3736_REG_CSPULLUP 0x10 // PG3
#define ISSI_REG_CONFIGURATION 0x00 // PG3
#define ISSI_REG_GLOBALCURRENT 0x01 // PG3
#define ISSI_REG_RESET 0x11 // PG3
#define ISSI_REG_SWPULLUP 0x0F // PG3
#define ISSI_REG_CSPULLUP 0x10 // PG3
#ifndef ISSI_TIMEOUT
# define ISSI_TIMEOUT 100
#ifndef IS31FL3736_I2C_TIMEOUT
# define IS31FL3736_I2C_TIMEOUT 100
#endif
#ifndef ISSI_PERSISTENCE
# define ISSI_PERSISTENCE 0
#ifndef IS31FL3736_I2C_PERSISTENCE
# define IS31FL3736_I2C_PERSISTENCE 0
#endif
#ifndef ISSI_SWPULLUP
# define ISSI_SWPULLUP PUR_0R
#ifndef IS31FL3736_PWM_FREQUENCY
# define IS31FL3736_PWM_FREQUENCY IS31FL3736_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3736B only
#endif
#ifndef ISSI_CSPULLUP
# define ISSI_CSPULLUP PUR_0R
#ifndef IS31FL3736_SWPULLUP
# define IS31FL3736_SWPULLUP IS31FL3736_PUR_0R
#endif
#ifndef ISSI_GLOBALCURRENT
# define ISSI_GLOBALCURRENT 0xFF
#ifndef IS31FL3736_CSPULLUP
# define IS31FL3736_CSPULLUP IS31FL3736_PUR_0R
#endif
#ifndef IS31FL3736_GLOBALCURRENT
# define IS31FL3736_GLOBALCURRENT 0xFF
#endif
// Transfer buffer for TWITransmitData()
@ -76,22 +69,22 @@ uint8_t g_twi_transfer_buffer[20];
// We could optimize this and take out the unused registers from these
// 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};
uint8_t g_pwm_buffer[IS31FL3736_DRIVER_COUNT][192];
bool g_pwm_buffer_update_required[IS31FL3736_DRIVER_COUNT] = {false};
uint8_t g_led_control_registers[DRIVER_COUNT][24] = {{0}, {0}};
bool g_led_control_registers_update_required = false;
uint8_t g_led_control_registers[IS31FL3736_DRIVER_COUNT][24] = {0};
bool g_led_control_registers_update_required[IS31FL3736_DRIVER_COUNT] = {false};
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;
#if ISSI_PERSISTENCE > 0
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) == 0) break;
#if IS31FL3736_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3736_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3736_I2C_TIMEOUT) == 0) break;
}
#else
i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT);
i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3736_I2C_TIMEOUT);
#endif
}
@ -109,12 +102,12 @@ void is31fl3736_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
// thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer
memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
#if ISSI_PERSISTENCE > 0
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) == 0) break;
#if IS31FL3736_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3736_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3736_I2C_TIMEOUT) == 0) break;
}
#else
i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT);
i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3736_I2C_TIMEOUT);
#endif
}
}
@ -126,20 +119,20 @@ void is31fl3736_init(uint8_t addr) {
// then disable software shutdown.
// Unlock the command register.
is31fl3736_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG0
is31fl3736_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER, IS31FL3736_PAGE_LEDCONTROL);
// Turn off all LEDs.
for (int i = 0x00; i <= 0x17; i++) {
is31fl3736_write_register(addr, i, 0x00);
}
// Unlock the command register.
is31fl3736_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG1
is31fl3736_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER, IS31FL3736_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++) {
@ -147,27 +140,27 @@ void is31fl3736_init(uint8_t addr) {
}
// Unlock the command register.
is31fl3736_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG3
is31fl3736_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER, IS31FL3736_PAGE_FUNCTION);
// Set de-ghost pull-up resistors (SWx)
is31fl3736_write_register(addr, ISSI_REG_SWPULLUP, ISSI_SWPULLUP);
is31fl3736_write_register(addr, IS31FL3736_REG_SWPULLUP, IS31FL3736_SWPULLUP);
// Set de-ghost pull-down resistors (CSx)
is31fl3736_write_register(addr, ISSI_REG_CSPULLUP, ISSI_CSPULLUP);
is31fl3736_write_register(addr, IS31FL3736_REG_CSPULLUP, IS31FL3736_CSPULLUP);
// Set global current to maximum.
is31fl3736_write_register(addr, ISSI_REG_GLOBALCURRENT, ISSI_GLOBALCURRENT);
is31fl3736_write_register(addr, IS31FL3736_REG_GLOBALCURRENT, IS31FL3736_GLOBALCURRENT);
// Disable software shutdown.
is31fl3736_write_register(addr, ISSI_REG_CONFIGURATION, 0x01);
is31fl3736_write_register(addr, IS31FL3736_REG_CONFIGURATION, ((IS31FL3736_PWM_FREQUENCY & 0b111) << 3) | 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) {
is31_led led;
is31fl3736_led_t led;
if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
memcpy_P(&led, (&g_is31fl3736_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;
@ -186,21 +179,14 @@ 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) {
is31_led led;
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
is31fl3736_led_t led;
memcpy_P(&led, (&g_is31fl3736_leds[index]), sizeof(led));
// IS31FL3733
// The PWM register for a matrix position (0x00 to 0xBF) can be
// divided by 8 to get the LED control register (0x00 to 0x17),
// then mod 8 to get the bit position within that register.
// IS31FL3736
// The PWM register for a matrix position (0x00 to 0xBF) is interleaved, so:
// A1=0x00 A2=0x02 A3=0x04 A4=0x06 A5=0x08 A6=0x0A A7=0x0C A8=0x0E
// B1=0x10 B2=0x12 B3=0x14
// But also, the LED control registers (0x00 to 0x17) are also interleaved, so:
// A1-A4=0x00 A5-A8=0x01
// So, the same math applies.
uint8_t control_register_r = led.r / 8;
uint8_t control_register_g = led.g / 8;
@ -226,63 +212,28 @@ void is31fl3736_set_led_control_register(uint8_t index, bool red, bool green, bo
g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);
}
g_led_control_registers_update_required = true;
}
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)
uint8_t pwm_register = index * 2;
g_pwm_buffer[0][pwm_register] = value;
g_pwm_buffer_update_required[0] = true;
}
}
void is31fl3736_mono_set_brightness_all(uint8_t value) {
for (int i = 0; i < 96; i++) {
is31fl3736_mono_set_brightness(i, value);
}
}
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)
uint8_t pwm_register = index * 2;
// Map register 0x00..0xBE (interleaved) into control register and bit
uint8_t control_register = pwm_register / 8;
uint8_t bit = pwm_register % 8;
if (enabled) {
g_led_control_registers[0][control_register] |= (1 << bit);
} else {
g_led_control_registers[0][control_register] &= ~(1 << bit);
}
g_led_control_registers_update_required = true;
g_led_control_registers_update_required[led.driver] = true;
}
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, IS31FL3736_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER, IS31FL3736_PAGE_PWM);
is31fl3736_write_pwm_buffer(addr, g_pwm_buffer[index]);
g_pwm_buffer_update_required[index] = false;
}
g_pwm_buffer_update_required[index] = false;
}
void is31fl3736_update_led_control_registers(uint8_t addr1, uint8_t addr2) {
if (g_led_control_registers_update_required) {
void is31fl3736_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
is31fl3736_write_register(addr1, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3736_write_register(addr1, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3736_write_register(addr, IS31FL3736_COMMANDREGISTER, IS31FL3736_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(addr, i, g_led_control_registers[index][i]);
}
g_led_control_registers_update_required = false;
g_led_control_registers_update_required[index] = false;
}
}

View File

@ -19,28 +19,66 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "progmem.h"
// Simple interface option.
// If these aren't defined, just define them to make it compile
#ifndef DRIVER_COUNT
# define DRIVER_COUNT 2
// ======== DEPRECATED DEFINES - DO NOT USE ========
#ifdef DRIVER_COUNT
# define IS31FL3736_DRIVER_COUNT DRIVER_COUNT
#endif
#ifdef ISSI_TIMEOUT
# define IS31FL3736_I2C_TIMEOUT ISSI_TIMEOUT
#endif
#ifdef ISSI_PERSISTENCE
# define IS31FL3736_I2C_PERSISTENCE ISSI_PERSISTENCE
#endif
#ifdef ISSI_SWPULLUP
# define IS31FL3736_SWPULLUP ISSI_SWPULLUP
#endif
#ifdef ISSI_CSPULLUP
# define IS31FL3736_CSPULLUP ISSI_CSPULLUP
#endif
#ifdef ISSI_GLOBALCURRENT
# define IS31FL3736_GLOBALCURRENT ISSI_GLOBALCURRENT
#endif
#ifndef RGB_MATRIX_LED_COUNT
# define RGB_MATRIX_LED_COUNT 96
#endif
#define is31_led is31fl3736_led_t
#define g_is31_leds g_is31fl3736_leds
typedef struct is31_led {
#define PUR_0R IS31FL3736_PUR_0R
#define PUR_05KR IS31FL3736_PUR_05KR
#define PUR_1KR IS31FL3736_PUR_1KR
#define PUR_2KR IS31FL3736_PUR_2KR
#define PUR_4KR IS31FL3736_PUR_4KR
#define PUR_8KR IS31FL3736_PUR_8KR
#define PUR_16KR IS31FL3736_PUR_16KR
#define PUR_32KR IS31FL3736_PUR_32KR
// ========
#define IS31FL3736_I2C_ADDRESS_GND_GND 0x50
#define IS31FL3736_I2C_ADDRESS_GND_SCL 0x51
#define IS31FL3736_I2C_ADDRESS_GND_SDA 0x52
#define IS31FL3736_I2C_ADDRESS_GND_VCC 0x53
#define IS31FL3736_I2C_ADDRESS_SCL_GND 0x54
#define IS31FL3736_I2C_ADDRESS_SCL_SCL 0x55
#define IS31FL3736_I2C_ADDRESS_SCL_SDA 0x56
#define IS31FL3736_I2C_ADDRESS_SCL_VCC 0x57
#define IS31FL3736_I2C_ADDRESS_SDA_GND 0x58
#define IS31FL3736_I2C_ADDRESS_SDA_SCL 0x59
#define IS31FL3736_I2C_ADDRESS_SDA_SDA 0x5A
#define IS31FL3736_I2C_ADDRESS_SDA_VCC 0x5B
#define IS31FL3736_I2C_ADDRESS_VCC_GND 0x5C
#define IS31FL3736_I2C_ADDRESS_VCC_SCL 0x5D
#define IS31FL3736_I2C_ADDRESS_VCC_SDA 0x5E
#define IS31FL3736_I2C_ADDRESS_VCC_VCC 0x5F
typedef struct is31fl3736_led_t {
uint8_t driver : 2;
uint8_t r;
uint8_t g;
uint8_t b;
} __attribute__((packed)) is31_led;
} __attribute__((packed)) is31fl3736_led_t;
extern const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT];
extern const is31fl3736_led_t PROGMEM g_is31fl3736_leds[RGB_MATRIX_LED_COUNT];
void is31fl3736_init(uint8_t addr);
void is31fl3736_write_register(uint8_t addr, uint8_t reg, uint8_t data);
@ -51,10 +89,6 @@ 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_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).
@ -62,14 +96,20 @@ void is31fl3736_mono_set_led_control_register(uint8_t index, bool enabled);
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
#define PUR_1KR 0x02 // 1.0k Ohm resistor
#define PUR_2KR 0x03 // 2.0k Ohm resistor
#define PUR_4KR 0x04 // 4.0k Ohm resistor
#define PUR_8KR 0x05 // 8.0k Ohm resistor
#define PUR_16KR 0x06 // 16k Ohm resistor
#define PUR_32KR 0x07 // 32k Ohm resistor
#define IS31FL3736_PUR_0R 0x00 // No PUR resistor
#define IS31FL3736_PUR_05KR 0x01 // 0.5k Ohm resistor
#define IS31FL3736_PUR_1KR 0x02 // 1.0k Ohm resistor
#define IS31FL3736_PUR_2KR 0x03 // 2.0k Ohm resistor
#define IS31FL3736_PUR_4KR 0x04 // 4.0k Ohm resistor
#define IS31FL3736_PUR_8KR 0x05 // 8.0k Ohm resistor
#define IS31FL3736_PUR_16KR 0x06 // 16k Ohm resistor
#define IS31FL3736_PUR_32KR 0x07 // 32k Ohm resistor
#define IS31FL3736_PWM_FREQUENCY_8K4_HZ 0b000
#define IS31FL3736_PWM_FREQUENCY_4K2_HZ 0b001
#define IS31FL3736_PWM_FREQUENCY_26K7_HZ 0b010
#define IS31FL3736_PWM_FREQUENCY_2K1_HZ 0b011
#define IS31FL3736_PWM_FREQUENCY_1K05_HZ 0b100
#define A_1 0x00
#define A_2 0x02

View File

@ -0,0 +1,219 @@
/* Copyright 2017 Jason Williams
* Copyright 2018 Jack Humbert
* Copyright 2018 Yiancar
* Copyright 2021 Doni Crosby
*
* 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 "is31fl3737-simple.h"
#include <string.h>
#include "i2c_master.h"
#include "wait.h"
#define IS31FL3737_COMMANDREGISTER 0xFD
#define IS31FL3737_COMMANDREGISTER_WRITELOCK 0xFE
#define IS31FL3737_INTERRUPTMASKREGISTER 0xF0
#define IS31FL3737_INTERRUPTSTATUSREGISTER 0xF1
#define IS31FL3737_PAGE_LEDCONTROL 0x00 // PG0
#define IS31FL3737_PAGE_PWM 0x01 // PG1
#define IS31FL3737_PAGE_AUTOBREATH 0x02 // PG2
#define IS31FL3737_PAGE_FUNCTION 0x03 // PG3
#define IS31FL3737_REG_CONFIGURATION 0x00 // PG3
#define IS31FL3737_REG_GLOBALCURRENT 0x01 // PG3
#define IS31FL3737_REG_RESET 0x11 // PG3
#define IS31FL3737_REG_SWPULLUP 0x0F // PG3
#define IS31FL3737_REG_CSPULLUP 0x10 // PG3
#ifndef IS31FL3737_I2C_TIMEOUT
# define IS31FL3737_I2C_TIMEOUT 100
#endif
#ifndef IS31FL3737_I2C_PERSISTENCE
# define IS31FL3737_I2C_PERSISTENCE 0
#endif
#ifndef IS31FL3737_PWM_FREQUENCY
# define IS31FL3737_PWM_FREQUENCY IS31FL3737_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3737B only
#endif
#ifndef IS31FL3737_SWPULLUP
# define IS31FL3737_SWPULLUP IS31FL3737_PUR_0R
#endif
#ifndef IS31FL3737_CSPULLUP
# define IS31FL3737_CSPULLUP IS31FL3737_PUR_0R
#endif
#ifndef IS31FL3737_GLOBALCURRENT
# define IS31FL3737_GLOBALCURRENT 0xFF
#endif
// Transfer buffer for TWITransmitData()
uint8_t g_twi_transfer_buffer[20];
// These buffers match the IS31FL3737 PWM registers.
// 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
// probably not worth the extra complexity.
uint8_t g_pwm_buffer[IS31FL3737_DRIVER_COUNT][192];
bool g_pwm_buffer_update_required[IS31FL3737_DRIVER_COUNT] = {false};
uint8_t g_led_control_registers[IS31FL3737_DRIVER_COUNT][24] = {0};
bool g_led_control_registers_update_required[IS31FL3737_DRIVER_COUNT] = {false};
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;
#if IS31FL3737_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3737_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3737_I2C_TIMEOUT) == 0) break;
}
#else
i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3737_I2C_TIMEOUT);
#endif
}
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
// g_twi_transfer_buffer[] is 20 bytes
// iterate over the pwm_buffer contents at 16 byte intervals
for (int i = 0; i < 192; i += 16) {
g_twi_transfer_buffer[0] = i;
// copy the data from i to i+15
// device will auto-increment register for data after the first byte
// thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer
memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
#if IS31FL3737_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3737_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3737_I2C_TIMEOUT) == 0) break;
}
#else
i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3737_I2C_TIMEOUT);
#endif
}
}
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, IS31FL3737_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG0
is31fl3737_write_register(addr, IS31FL3737_COMMANDREGISTER, IS31FL3737_PAGE_LEDCONTROL);
// Turn off all LEDs.
for (int i = 0x00; i <= 0x17; i++) {
is31fl3737_write_register(addr, i, 0x00);
}
// Unlock the command register.
is31fl3737_write_register(addr, IS31FL3737_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG1
is31fl3737_write_register(addr, IS31FL3737_COMMANDREGISTER, IS31FL3737_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);
}
// Unlock the command register.
is31fl3737_write_register(addr, IS31FL3737_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG3
is31fl3737_write_register(addr, IS31FL3737_COMMANDREGISTER, IS31FL3737_PAGE_FUNCTION);
// Set de-ghost pull-up resistors (SWx)
is31fl3737_write_register(addr, IS31FL3737_REG_SWPULLUP, IS31FL3737_SWPULLUP);
// Set de-ghost pull-down resistors (CSx)
is31fl3737_write_register(addr, IS31FL3737_REG_CSPULLUP, IS31FL3737_CSPULLUP);
// Set global current to maximum.
is31fl3737_write_register(addr, IS31FL3737_REG_GLOBALCURRENT, IS31FL3737_GLOBALCURRENT);
// Disable software shutdown.
is31fl3737_write_register(addr, IS31FL3737_REG_CONFIGURATION, ((IS31FL3737_PWM_FREQUENCY & 0b111) << 3) | 0x01);
// Wait 10ms to ensure the device has woken up.
wait_ms(10);
}
void is31fl3737_set_value(int index, uint8_t value) {
is31fl3737_led_t led;
if (index >= 0 && index < LED_MATRIX_LED_COUNT) {
memcpy_P(&led, (&g_is31fl3737_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 is31fl3737_set_value_all(uint8_t value) {
for (int i = 0; i < LED_MATRIX_LED_COUNT; i++) {
is31fl3737_set_value(i, value);
}
}
void is31fl3737_set_led_control_register(uint8_t index, bool value) {
is31fl3737_led_t led;
memcpy_P(&led, (&g_is31fl3737_leds[index]), sizeof(led));
uint8_t control_register = led.v / 8;
uint8_t bit_value = led.v % 8;
if (value) {
g_led_control_registers[led.driver][control_register] |= (1 << bit_value);
} else {
g_led_control_registers[led.driver][control_register] &= ~(1 << bit_value);
}
g_led_control_registers_update_required[led.driver] = true;
}
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, IS31FL3737_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3737_write_register(addr, IS31FL3737_COMMANDREGISTER, IS31FL3737_PAGE_PWM);
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) {
if (g_led_control_registers_update_required[index]) {
// Firstly we need to unlock the command register and select PG0
is31fl3737_write_register(addr, IS31FL3737_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3737_write_register(addr, IS31FL3737_COMMANDREGISTER, IS31FL3737_PAGE_LEDCONTROL);
for (int i = 0; i < 24; i++) {
is31fl3737_write_register(addr, i, g_led_control_registers[index][i]);
}
g_led_control_registers_update_required[index] = false;
}
}

View File

@ -0,0 +1,256 @@
/* Copyright 2017 Jason Williams
* Copyright 2018 Jack Humbert
* Copyright 2018 Yiancar
* Copyright 2021 Doni Crosby
*
* 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>
#include <stdbool.h>
#include "progmem.h"
// ======== DEPRECATED DEFINES - DO NOT USE ========
#ifdef DRIVER_COUNT
# define IS31FL3737_DRIVER_COUNT DRIVER_COUNT
#endif
#ifdef ISSI_TIMEOUT
# define IS31FL3737_I2C_TIMEOUT ISSI_TIMEOUT
#endif
#ifdef ISSI_PERSISTENCE
# define IS31FL3737_I2C_PERSISTENCE ISSI_PERSISTENCE
#endif
#ifdef ISSI_PWM_FREQUENCY
# define IS31FL3737_PWM_FREQUENCY ISSI_PWM_FREQUENCY
#endif
#ifdef ISSI_SWPULLUP
# define IS31FL3737_SWPULLUP ISSI_SWPULLUP
#endif
#ifdef ISSI_CSPULLUP
# define IS31FL3737_CSPULLUP ISSI_CSPULLUP
#endif
#ifdef ISSI_GLOBALCURRENT
# define IS31FL3737_GLOBALCURRENT ISSI_GLOBALCURRENT
#endif
#define PUR_0R IS31FL3737_PUR_0R
#define PUR_05KR IS31FL3737_PUR_05KR
#define PUR_1KR IS31FL3737_PUR_1KR
#define PUR_2KR IS31FL3737_PUR_2KR
#define PUR_4KR IS31FL3737_PUR_4KR
#define PUR_8KR IS31FL3737_PUR_8KR
#define PUR_16KR IS31FL3737_PUR_16KR
#define PUR_32KR IS31FL3737_PUR_32KR
// ========
#define IS31FL3737_I2C_ADDRESS_GND 0x50
#define IS31FL3737_I2C_ADDRESS_SCL 0x55
#define IS31FL3737_I2C_ADDRESS_SDA 0x5A
#define IS31FL3737_I2C_ADDRESS_VCC 0x5F
typedef struct is31fl3737_led_t {
uint8_t driver : 2;
uint8_t v;
} __attribute__((packed)) is31fl3737_led_t;
extern const is31fl3737_led_t PROGMEM g_is31fl3737_leds[LED_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_set_value(int index, uint8_t value);
void is31fl3737_set_value_all(uint8_t value);
void is31fl3737_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 is31fl3737_update_pwm_buffers(uint8_t addr, uint8_t index);
void is31fl3737_update_led_control_registers(uint8_t addr, uint8_t index);
#define IS31FL3737_PUR_0R 0x00 // No PUR resistor
#define IS31FL3737_PUR_05KR 0x01 // 0.5k Ohm resistor in t_NOL
#define IS31FL3737_PUR_1KR 0x02 // 1.0k Ohm resistor in t_NOL
#define IS31FL3737_PUR_2KR 0x03 // 2.0k Ohm resistor in t_NOL
#define IS31FL3737_PUR_4KR 0x04 // 4.0k Ohm resistor in t_NOL
#define IS31FL3737_PUR_8KR 0x05 // 8.0k Ohm resistor in t_NOL
#define IS31FL3737_PUR_16KR 0x06 // 16k Ohm resistor in t_NOL
#define IS31FL3737_PUR_32KR 0x07 // 32k Ohm resistor in t_NOL
#define IS31FL3737_PWM_FREQUENCY_8K4_HZ 0b000
#define IS31FL3737_PWM_FREQUENCY_4K2_HZ 0b001
#define IS31FL3737_PWM_FREQUENCY_26K7_HZ 0b010
#define IS31FL3737_PWM_FREQUENCY_2K1_HZ 0b011
#define IS31FL3737_PWM_FREQUENCY_1K05_HZ 0b100
#define A_1 0x00
#define A_2 0x01
#define A_3 0x02
#define A_4 0x03
#define A_5 0x04
#define A_6 0x05
#define A_7 0x08
#define A_8 0x09
#define A_9 0x0A
#define A_10 0x0B
#define A_11 0x0C
#define A_12 0x0D
#define B_1 0x10
#define B_2 0x11
#define B_3 0x12
#define B_4 0x13
#define B_5 0x14
#define B_6 0x15
#define B_7 0x18
#define B_8 0x19
#define B_9 0x1A
#define B_10 0x1B
#define B_11 0x1C
#define B_12 0x1D
#define C_1 0x20
#define C_2 0x21
#define C_3 0x22
#define C_4 0x23
#define C_5 0x24
#define C_6 0x25
#define C_7 0x28
#define C_8 0x29
#define C_9 0x2A
#define C_10 0x2B
#define C_11 0x2C
#define C_12 0x2D
#define D_1 0x30
#define D_2 0x31
#define D_3 0x32
#define D_4 0x33
#define D_5 0x34
#define D_6 0x35
#define D_7 0x38
#define D_8 0x39
#define D_9 0x3A
#define D_10 0x3B
#define D_11 0x3C
#define D_12 0x3D
#define E_1 0x40
#define E_2 0x41
#define E_3 0x42
#define E_4 0x43
#define E_5 0x44
#define E_6 0x45
#define E_7 0x48
#define E_8 0x49
#define E_9 0x4A
#define E_10 0x4B
#define E_11 0x4C
#define E_12 0x4D
#define F_1 0x50
#define F_2 0x51
#define F_3 0x52
#define F_4 0x53
#define F_5 0x54
#define F_6 0x55
#define F_7 0x58
#define F_8 0x59
#define F_9 0x5A
#define F_10 0x5B
#define F_11 0x5C
#define F_12 0x5D
#define G_1 0x60
#define G_2 0x61
#define G_3 0x62
#define G_4 0x63
#define G_5 0x64
#define G_6 0x65
#define G_7 0x68
#define G_8 0x69
#define G_9 0x6A
#define G_10 0x6B
#define G_11 0x6C
#define G_12 0x6D
#define H_1 0x70
#define H_2 0x71
#define H_3 0x72
#define H_4 0x73
#define H_5 0x74
#define H_6 0x75
#define H_7 0x78
#define H_8 0x79
#define H_9 0x7A
#define H_10 0x7B
#define H_11 0x7C
#define H_12 0x7D
#define I_1 0x80
#define I_2 0x81
#define I_3 0x82
#define I_4 0x83
#define I_5 0x84
#define I_6 0x85
#define I_7 0x88
#define I_8 0x89
#define I_9 0x8A
#define I_10 0x8B
#define I_11 0x8C
#define I_12 0x8D
#define J_1 0x90
#define J_2 0x91
#define J_3 0x92
#define J_4 0x93
#define J_5 0x94
#define J_6 0x95
#define J_7 0x98
#define J_8 0x99
#define J_9 0x9A
#define J_10 0x9B
#define J_11 0x9C
#define J_12 0x9D
#define K_1 0xA0
#define K_2 0xA1
#define K_3 0xA2
#define K_4 0xA3
#define K_5 0xA4
#define K_6 0xA5
#define K_7 0xA8
#define K_8 0xA9
#define K_9 0xAA
#define K_10 0xAB
#define K_11 0xAC
#define K_12 0xAD
#define L_1 0xB0
#define L_2 0xB1
#define L_3 0xB2
#define L_4 0xB3
#define L_5 0xB4
#define L_6 0xB5
#define L_7 0xB8
#define L_8 0xB9
#define L_9 0xBA
#define L_10 0xBB
#define L_11 0xBC
#define L_12 0xBD

View File

@ -18,59 +18,48 @@
*/
#include "is31fl3737.h"
#include <string.h>
#include "i2c_master.h"
#include "wait.h"
// This is a 7-bit address, that gets left-shifted and bit 0
// set to 0 for write, 1 for read (as per I2C protocol)
// The address will vary depending on your wiring:
// 00 <-> GND
// 01 <-> SCL
// 10 <-> SDA
// 11 <-> VCC
// ADDR1 represents A1:A0 of the 7-bit address.
// ADDR2 represents A3:A2 of the 7-bit address.
// The result is: 0b101(ADDR2)(ADDR1)
#define ISSI_ADDR_DEFAULT 0x50
#define IS31FL3737_COMMANDREGISTER 0xFD
#define IS31FL3737_COMMANDREGISTER_WRITELOCK 0xFE
#define IS31FL3737_INTERRUPTMASKREGISTER 0xF0
#define IS31FL3737_INTERRUPTSTATUSREGISTER 0xF1
#define ISSI_COMMANDREGISTER 0xFD
#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE
#define ISSI_INTERRUPTMASKREGISTER 0xF0
#define ISSI_INTERRUPTSTATUSREGISTER 0xF1
#define IS31FL3737_PAGE_LEDCONTROL 0x00 // PG0
#define IS31FL3737_PAGE_PWM 0x01 // PG1
#define IS31FL3737_PAGE_AUTOBREATH 0x02 // PG2
#define IS31FL3737_PAGE_FUNCTION 0x03 // PG3
#define ISSI_PAGE_LEDCONTROL 0x00 // PG0
#define ISSI_PAGE_PWM 0x01 // PG1
#define ISSI_PAGE_AUTOBREATH 0x02 // PG2
#define ISSI_PAGE_FUNCTION 0x03 // PG3
#define IS31FL3737_REG_CONFIGURATION 0x00 // PG3
#define IS31FL3737_REG_GLOBALCURRENT 0x01 // PG3
#define IS31FL3737_REG_RESET 0x11 // PG3
#define IS31FL3737_REG_SWPULLUP 0x0F // PG3
#define IS31FL3737_REG_CSPULLUP 0x10 // PG3
#define ISSI_REG_CONFIGURATION 0x00 // PG3
#define ISSI_REG_GLOBALCURRENT 0x01 // PG3
#define ISSI_REG_RESET 0x11 // PG3
#define ISSI_REG_SWPULLUP 0x0F // PG3
#define ISSI_REG_CSPULLUP 0x10 // PG3
#ifndef ISSI_TIMEOUT
# define ISSI_TIMEOUT 100
#ifndef IS31FL3737_I2C_TIMEOUT
# define IS31FL3737_I2C_TIMEOUT 100
#endif
#ifndef ISSI_PERSISTENCE
# define ISSI_PERSISTENCE 0
#ifndef IS31FL3737_I2C_PERSISTENCE
# define IS31FL3737_I2C_PERSISTENCE 0
#endif
#ifndef ISSI_PWM_FREQUENCY
# define ISSI_PWM_FREQUENCY 0b000 // PFS - IS31FL3737B only
#ifndef IS31FL3737_PWM_FREQUENCY
# define IS31FL3737_PWM_FREQUENCY IS31FL3737_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3737B only
#endif
#ifndef ISSI_SWPULLUP
# define ISSI_SWPULLUP PUR_0R
#ifndef IS31FL3737_SWPULLUP
# define IS31FL3737_SWPULLUP IS31FL3737_PUR_0R
#endif
#ifndef ISSI_CSPULLUP
# define ISSI_CSPULLUP PUR_0R
#ifndef IS31FL3737_CSPULLUP
# define IS31FL3737_CSPULLUP IS31FL3737_PUR_0R
#endif
#ifndef ISSI_GLOBALCURRENT
# define ISSI_GLOBALCURRENT 0xFF
#ifndef IS31FL3737_GLOBALCURRENT
# define IS31FL3737_GLOBALCURRENT 0xFF
#endif
// Transfer buffer for TWITransmitData()
@ -83,22 +72,22 @@ uint8_t g_twi_transfer_buffer[20];
// 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];
bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
uint8_t g_pwm_buffer[IS31FL3737_DRIVER_COUNT][192];
bool g_pwm_buffer_update_required[IS31FL3737_DRIVER_COUNT] = {false};
uint8_t g_led_control_registers[DRIVER_COUNT][24] = {0};
bool g_led_control_registers_update_required[DRIVER_COUNT] = {false};
uint8_t g_led_control_registers[IS31FL3737_DRIVER_COUNT][24] = {0};
bool g_led_control_registers_update_required[IS31FL3737_DRIVER_COUNT] = {false};
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;
#if ISSI_PERSISTENCE > 0
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) == 0) break;
#if IS31FL3737_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3737_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3737_I2C_TIMEOUT) == 0) break;
}
#else
i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT);
i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3737_I2C_TIMEOUT);
#endif
}
@ -116,12 +105,12 @@ void is31fl3737_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
// thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer
memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 16);
#if ISSI_PERSISTENCE > 0
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) == 0) break;
#if IS31FL3737_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3737_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3737_I2C_TIMEOUT) == 0) break;
}
#else
i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT);
i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, IS31FL3737_I2C_TIMEOUT);
#endif
}
}
@ -133,20 +122,20 @@ void is31fl3737_init(uint8_t addr) {
// then disable software shutdown.
// Unlock the command register.
is31fl3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3737_write_register(addr, IS31FL3737_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG0
is31fl3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
is31fl3737_write_register(addr, IS31FL3737_COMMANDREGISTER, IS31FL3737_PAGE_LEDCONTROL);
// Turn off all LEDs.
for (int i = 0x00; i <= 0x17; i++) {
is31fl3737_write_register(addr, i, 0x00);
}
// Unlock the command register.
is31fl3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3737_write_register(addr, IS31FL3737_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG1
is31fl3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
is31fl3737_write_register(addr, IS31FL3737_COMMANDREGISTER, IS31FL3737_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++) {
@ -154,27 +143,27 @@ void is31fl3737_init(uint8_t addr) {
}
// Unlock the command register.
is31fl3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3737_write_register(addr, IS31FL3737_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG3
is31fl3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
is31fl3737_write_register(addr, IS31FL3737_COMMANDREGISTER, IS31FL3737_PAGE_FUNCTION);
// Set de-ghost pull-up resistors (SWx)
is31fl3737_write_register(addr, ISSI_REG_SWPULLUP, ISSI_SWPULLUP);
is31fl3737_write_register(addr, IS31FL3737_REG_SWPULLUP, IS31FL3737_SWPULLUP);
// Set de-ghost pull-down resistors (CSx)
is31fl3737_write_register(addr, ISSI_REG_CSPULLUP, ISSI_CSPULLUP);
is31fl3737_write_register(addr, IS31FL3737_REG_CSPULLUP, IS31FL3737_CSPULLUP);
// Set global current to maximum.
is31fl3737_write_register(addr, ISSI_REG_GLOBALCURRENT, ISSI_GLOBALCURRENT);
is31fl3737_write_register(addr, IS31FL3737_REG_GLOBALCURRENT, IS31FL3737_GLOBALCURRENT);
// Disable software shutdown.
is31fl3737_write_register(addr, ISSI_REG_CONFIGURATION, ((ISSI_PWM_FREQUENCY & 0b111) << 3) | 0x01);
is31fl3737_write_register(addr, IS31FL3737_REG_CONFIGURATION, ((IS31FL3737_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) {
is31_led led;
is31fl3737_led_t led;
if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
memcpy_P(&led, (&g_is31fl3737_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;
@ -193,8 +182,8 @@ 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) {
is31_led led;
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
is31fl3737_led_t led;
memcpy_P(&led, (&g_is31fl3737_leds[index]), sizeof(led));
uint8_t control_register_r = led.r / 8;
uint8_t control_register_g = led.g / 8;
@ -225,22 +214,22 @@ void is31fl3737_set_led_control_register(uint8_t index, bool red, bool green, bo
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, IS31FL3737_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3737_write_register(addr, IS31FL3737_COMMANDREGISTER, IS31FL3737_PAGE_PWM);
is31fl3737_write_pwm_buffer(addr, g_pwm_buffer[index]);
g_pwm_buffer_update_required[index] = false;
}
g_pwm_buffer_update_required[index] = false;
}
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, IS31FL3737_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3737_write_register(addr, IS31FL3737_COMMANDREGISTER, IS31FL3737_PAGE_LEDCONTROL);
for (int i = 0; i < 24; i++) {
is31fl3737_write_register(addr, i, g_led_control_registers[index][i]);
}
g_led_control_registers_update_required[index] = false;
}
g_led_control_registers_update_required[index] = false;
}

View File

@ -21,17 +21,57 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "progmem.h"
typedef struct is31_led {
// ======== DEPRECATED DEFINES - DO NOT USE ========
#ifdef DRIVER_COUNT
# define IS31FL3737_DRIVER_COUNT DRIVER_COUNT
#endif
#ifdef ISSI_TIMEOUT
# define IS31FL3737_I2C_TIMEOUT ISSI_TIMEOUT
#endif
#ifdef ISSI_PERSISTENCE
# define IS31FL3737_I2C_PERSISTENCE ISSI_PERSISTENCE
#endif
#ifdef ISSI_PWM_FREQUENCY
# define IS31FL3737_PWM_FREQUENCY ISSI_PWM_FREQUENCY
#endif
#ifdef ISSI_SWPULLUP
# define IS31FL3737_SWPULLUP ISSI_SWPULLUP
#endif
#ifdef ISSI_CSPULLUP
# define IS31FL3737_CSPULLUP ISSI_CSPULLUP
#endif
#ifdef ISSI_GLOBALCURRENT
# define IS31FL3737_GLOBALCURRENT ISSI_GLOBALCURRENT
#endif
#define is31_led is31fl3737_led_t
#define g_is31_leds g_is31fl3737_leds
#define PUR_0R IS31FL3737_PUR_0R
#define PUR_05KR IS31FL3737_PUR_05KR
#define PUR_1KR IS31FL3737_PUR_1KR
#define PUR_2KR IS31FL3737_PUR_2KR
#define PUR_4KR IS31FL3737_PUR_4KR
#define PUR_8KR IS31FL3737_PUR_8KR
#define PUR_16KR IS31FL3737_PUR_16KR
#define PUR_32KR IS31FL3737_PUR_32KR
// ========
#define IS31FL3737_I2C_ADDRESS_GND 0x50
#define IS31FL3737_I2C_ADDRESS_SCL 0x55
#define IS31FL3737_I2C_ADDRESS_SDA 0x5A
#define IS31FL3737_I2C_ADDRESS_VCC 0x5F
typedef struct is31fl3737_led_t {
uint8_t driver : 2;
uint8_t r;
uint8_t g;
uint8_t b;
} __attribute__((packed)) is31_led;
} __attribute__((packed)) is31fl3737_led_t;
extern const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT];
extern const is31fl3737_led_t PROGMEM g_is31fl3737_leds[RGB_MATRIX_LED_COUNT];
void is31fl3737_init(uint8_t addr);
void is31fl3737_write_register(uint8_t addr, uint8_t reg, uint8_t data);
@ -49,14 +89,20 @@ void is31fl3737_set_led_control_register(uint8_t index, bool red, bool green, bo
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
#define PUR_1KR 0x02 // 1.0k Ohm resistor in t_NOL
#define PUR_2KR 0x03 // 2.0k Ohm resistor in t_NOL
#define PUR_4KR 0x04 // 4.0k Ohm resistor in t_NOL
#define PUR_8KR 0x05 // 8.0k Ohm resistor in t_NOL
#define PUR_16KR 0x06 // 16k Ohm resistor in t_NOL
#define PUR_32KR 0x07 // 32k Ohm resistor in t_NOL
#define IS31FL3737_PUR_0R 0x00 // No PUR resistor
#define IS31FL3737_PUR_05KR 0x01 // 0.5k Ohm resistor in t_NOL
#define IS31FL3737_PUR_1KR 0x02 // 1.0k Ohm resistor in t_NOL
#define IS31FL3737_PUR_2KR 0x03 // 2.0k Ohm resistor in t_NOL
#define IS31FL3737_PUR_4KR 0x04 // 4.0k Ohm resistor in t_NOL
#define IS31FL3737_PUR_8KR 0x05 // 8.0k Ohm resistor in t_NOL
#define IS31FL3737_PUR_16KR 0x06 // 16k Ohm resistor in t_NOL
#define IS31FL3737_PUR_32KR 0x07 // 32k Ohm resistor in t_NOL
#define IS31FL3737_PWM_FREQUENCY_8K4_HZ 0b000
#define IS31FL3737_PWM_FREQUENCY_4K2_HZ 0b001
#define IS31FL3737_PWM_FREQUENCY_26K7_HZ 0b010
#define IS31FL3737_PWM_FREQUENCY_2K1_HZ 0b011
#define IS31FL3737_PWM_FREQUENCY_1K05_HZ 0b100
#define A_1 0x00
#define A_2 0x01

View File

@ -0,0 +1,253 @@
/* Copyright 2017 Jason Williams
* Copyright 2018 Jack Humbert
* Copyright 2018 Yiancar
* Copyright 2020 MelGeek
*
* 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 "is31fl3741-simple.h"
#include <string.h>
#include "i2c_master.h"
#include "wait.h"
#define IS31FL3741_COMMANDREGISTER 0xFD
#define IS31FL3741_COMMANDREGISTER_WRITELOCK 0xFE
#define IS31FL3741_INTERRUPTMASKREGISTER 0xF0
#define IS31FL3741_INTERRUPTSTATUSREGISTER 0xF1
#define IS31FL3741_IDREGISTER 0xFC
#define IS31FL3741_PAGE_PWM0 0x00 // PG0
#define IS31FL3741_PAGE_PWM1 0x01 // PG1
#define IS31FL3741_PAGE_SCALING_0 0x02 // PG2
#define IS31FL3741_PAGE_SCALING_1 0x03 // PG3
#define IS31FL3741_PAGE_FUNCTION 0x04 // PG4
#define IS31FL3741_REG_CONFIGURATION 0x00 // PG4
#define IS31FL3741_REG_GLOBALCURRENT 0x01 // PG4
#define IS31FL3741_REG_PULLDOWNUP 0x02 // PG4
#define IS31FL3741_REG_PWM_FREQUENCY 0x36 // PG4
#define IS31FL3741_REG_RESET 0x3F // PG4
#ifndef IS31FL3741_I2C_TIMEOUT
# define IS31FL3741_I2C_TIMEOUT 100
#endif
#ifndef IS31FL3741_I2C_PERSISTENCE
# define IS31FL3741_I2C_PERSISTENCE 0
#endif
#ifndef IS31FL3741_CONFIGURATION
# define IS31FL3741_CONFIGURATION 0x01
#endif
#ifndef IS31FL3741_PWM_FREQUENCY
# define IS31FL3741_PWM_FREQUENCY IS31FL3741_PWM_FREQUENCY_29K_HZ
#endif
#ifndef IS31FL3741_SWPULLUP
# define IS31FL3741_SWPULLUP IS31FL3741_PUR_32KR
#endif
#ifndef IS31FL3741_CSPULLUP
# define IS31FL3741_CSPULLUP IS31FL3741_PUR_32KR
#endif
#ifndef IS31FL3741_GLOBALCURRENT
# define IS31FL3741_GLOBALCURRENT 0xFF
#endif
#define IS31FL3741_MAX_LEDS 351
// Transfer buffer for TWITransmitData()
uint8_t g_twi_transfer_buffer[20] = {0xFF};
// These buffers match the IS31FL3741 and IS31FL3741A PWM registers.
// 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
// probably not worth the extra complexity.
uint8_t g_pwm_buffer[IS31FL3741_DRIVER_COUNT][IS31FL3741_MAX_LEDS];
bool g_pwm_buffer_update_required[IS31FL3741_DRIVER_COUNT] = {false};
bool g_scaling_registers_update_required[IS31FL3741_DRIVER_COUNT] = {false};
uint8_t g_scaling_registers[IS31FL3741_DRIVER_COUNT][IS31FL3741_MAX_LEDS];
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;
#if IS31FL3741_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3741_I2C_TIMEOUT) == 0) break;
}
#else
i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3741_I2C_TIMEOUT);
#endif
}
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, IS31FL3741_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3741_write_register(addr, IS31FL3741_COMMANDREGISTER, IS31FL3741_PAGE_PWM1);
}
g_twi_transfer_buffer[0] = i % 180;
memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 18);
#if IS31FL3741_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 19, IS31FL3741_I2C_TIMEOUT) != 0) {
return false;
}
}
#else
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 19, IS31FL3741_I2C_TIMEOUT) != 0) {
return false;
}
#endif
}
// transfer the left cause the total number is 351
g_twi_transfer_buffer[0] = 162;
memcpy(g_twi_transfer_buffer + 1, pwm_buffer + 342, 9);
#if IS31FL3741_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 10, IS31FL3741_I2C_TIMEOUT) != 0) {
return false;
}
}
#else
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 10, IS31FL3741_I2C_TIMEOUT) != 0) {
return false;
}
#endif
return true;
}
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,
// then disable software shutdown.
// Unlock the command register.
// Unlock the command register.
is31fl3741_write_register(addr, IS31FL3741_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG4
is31fl3741_write_register(addr, IS31FL3741_COMMANDREGISTER, IS31FL3741_PAGE_FUNCTION);
// Set to Normal operation
is31fl3741_write_register(addr, IS31FL3741_REG_CONFIGURATION, IS31FL3741_CONFIGURATION);
// Set Golbal Current Control Register
is31fl3741_write_register(addr, IS31FL3741_REG_GLOBALCURRENT, IS31FL3741_GLOBALCURRENT);
// Set Pull up & Down for SWx CSy
is31fl3741_write_register(addr, IS31FL3741_REG_PULLDOWNUP, ((IS31FL3741_CSPULLUP << 4) | IS31FL3741_SWPULLUP));
// Set PWM frequency
is31fl3741_write_register(addr, IS31FL3741_REG_PWM_FREQUENCY, (IS31FL3741_PWM_FREQUENCY & 0b1111));
// is31fl3741_update_led_scaling_registers(addr, 0xFF, 0xFF, 0xFF);
// Wait 10ms to ensure the device has woken up.
wait_ms(10);
}
void is31fl3741_set_value(int index, uint8_t value) {
is31fl3741_led_t led;
if (index >= 0 && index < LED_MATRIX_LED_COUNT) {
memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led));
if (g_pwm_buffer[led.driver][led.v] == value) {
return;
}
g_pwm_buffer_update_required[led.driver] = true;
g_pwm_buffer[led.driver][led.v] = value;
}
}
void is31fl3741_set_value_all(uint8_t value) {
for (int i = 0; i < LED_MATRIX_LED_COUNT; i++) {
is31fl3741_set_value(i, value);
}
}
void is31fl3741_set_led_control_register(uint8_t index, bool value) {
is31fl3741_led_t led;
memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led));
if (value) {
g_scaling_registers[led.driver][led.v] = 0xFF;
} else {
g_scaling_registers[led.driver][led.v] = 0x00;
}
g_scaling_registers_update_required[led.driver] = true;
}
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, IS31FL3741_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3741_write_register(addr, IS31FL3741_COMMANDREGISTER, IS31FL3741_PAGE_PWM0);
is31fl3741_write_pwm_buffer(addr, g_pwm_buffer[index]);
}
g_pwm_buffer_update_required[index] = false;
}
void is31fl3741_set_pwm_buffer(const is31fl3741_led_t *pled, uint8_t value) {
g_pwm_buffer[pled->driver][pled->v] = value;
g_pwm_buffer_update_required[pled->driver] = true;
}
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, IS31FL3741_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3741_write_register(addr, IS31FL3741_COMMANDREGISTER, IS31FL3741_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]);
}
// unlock the command register and select PG3
is31fl3741_write_register(addr, IS31FL3741_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3741_write_register(addr, IS31FL3741_COMMANDREGISTER, IS31FL3741_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]);
}
g_scaling_registers_update_required[index] = false;
}
}
void is31fl3741_set_scaling_registers(const is31fl3741_led_t *pled, uint8_t value) {
g_scaling_registers[pled->driver][pled->v] = value;
g_scaling_registers_update_required[pled->driver] = true;
}

View File

@ -0,0 +1,471 @@
/* Copyright 2017 Jason Williams
* Copyright 2018 Jack Humbert
* Copyright 2018 Yiancar
* Copyright 2020 MelGeek
*
* 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>
#include <stdbool.h>
#include "progmem.h"
// ======== DEPRECATED DEFINES - DO NOT USE ========
#ifdef DRIVER_COUNT
# define IS31FL3741_DRIVER_COUNT DRIVER_COUNT
#endif
#ifdef ISSI_TIMEOUT
# define IS31FL3741_I2C_TIMEOUT ISSI_TIMEOUT
#endif
#ifdef ISSI_PERSISTENCE
# define IS31FL3741_I2C_PERSISTENCE ISSI_PERSISTENCE
#endif
#ifdef ISSI_CONFIGURATION
# define IS31FL3741_CONFIGURATION ISSI_CONFIGURATION
#endif
#ifdef ISSI_SWPULLUP
# define IS31FL3741_SWPULLUP ISSI_SWPULLUP
#endif
#ifdef ISSI_CSPULLUP
# define IS31FL3741_CSPULLUP ISSI_CSPULLUP
#endif
#ifdef ISSI_GLOBALCURRENT
# define IS31FL3741_GLOBALCURRENT ISSI_GLOBALCURRENT
#endif
#define PUR_0R IS31FL3741_PUR_0R
#define PUR_05KR IS31FL3741_PUR_05KR
#define PUR_1KR IS31FL3741_PUR_1KR
#define PUR_2KR IS31FL3741_PUR_2KR
#define PUR_4KR IS31FL3741_PUR_4KR
#define PUR_8KR IS31FL3741_PUR_8KR
#define PUR_16KR IS31FL3741_PUR_16KR
#define PUR_32KR IS31FL3741_PUR_32KR
// ========
#define IS31FL3741_I2C_ADDRESS_GND 0x30
#define IS31FL3741_I2C_ADDRESS_SCL 0x31
#define IS31FL3741_I2C_ADDRESS_SDA 0x32
#define IS31FL3741_I2C_ADDRESS_VCC 0x33
typedef struct is31fl3741_led_t {
uint32_t driver : 2;
uint32_t v : 10;
} __attribute__((packed)) is31fl3741_led_t;
extern const is31fl3741_led_t PROGMEM g_is31fl3741_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_set_value(int index, uint8_t value);
void is31fl3741_set_value_all(uint8_t value);
void is31fl3741_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 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 is31fl3741_led_t *pled, uint8_t value);
void is31fl3741_set_pwm_buffer(const is31fl3741_led *pled, uint8_t value);
#define IS31FL3741_PUR_0R 0x00 // No PUR resistor
#define IS31FL3741_PUR_05KR 0x01 // 0.5k Ohm resistor
#define IS31FL3741_PUR_1KR 0x02 // 1.0k Ohm resistor
#define IS31FL3741_PUR_2KR 0x03 // 2.0k Ohm resistor
#define IS31FL3741_PUR_4KR 0x04 // 4.0k Ohm resistor
#define IS31FL3741_PUR_8KR 0x05 // 8.0k Ohm resistor
#define IS31FL3741_PUR_16KR 0x06 // 16k Ohm resistor
#define IS31FL3741_PUR_32KR 0x07 // 32k Ohm resistor
#define IS31FL3741_PWM_FREQUENCY_29K_HZ 0b0000
#define IS31FL3741_PWM_FREQUENCY_3K6_HZ 0b0011
#define IS31FL3741_PWM_FREQUENCY_1K8_HZ 0b0111
#define IS31FL3741_PWM_FREQUENCY_900_HZ 0b1011
#define CS1_SW1 0x00
#define CS2_SW1 0x01
#define CS3_SW1 0x02
#define CS4_SW1 0x03
#define CS5_SW1 0x04
#define CS6_SW1 0x05
#define CS7_SW1 0x06
#define CS8_SW1 0x07
#define CS9_SW1 0x08
#define CS10_SW1 0x09
#define CS11_SW1 0x0A
#define CS12_SW1 0x0B
#define CS13_SW1 0x0C
#define CS14_SW1 0x0D
#define CS15_SW1 0x0E
#define CS16_SW1 0x0F
#define CS17_SW1 0x10
#define CS18_SW1 0x11
#define CS19_SW1 0x12
#define CS20_SW1 0x13
#define CS21_SW1 0x14
#define CS22_SW1 0x15
#define CS23_SW1 0x16
#define CS24_SW1 0x17
#define CS25_SW1 0x18
#define CS26_SW1 0x19
#define CS27_SW1 0x1A
#define CS28_SW1 0x1B
#define CS29_SW1 0x1C
#define CS30_SW1 0x1D
#define CS1_SW2 0x1E
#define CS2_SW2 0x1F
#define CS3_SW2 0x20
#define CS4_SW2 0x21
#define CS5_SW2 0x22
#define CS6_SW2 0x23
#define CS7_SW2 0x24
#define CS8_SW2 0x25
#define CS9_SW2 0x26
#define CS10_SW2 0x27
#define CS11_SW2 0x28
#define CS12_SW2 0x29
#define CS13_SW2 0x2A
#define CS14_SW2 0x2B
#define CS15_SW2 0x2C
#define CS16_SW2 0x2D
#define CS17_SW2 0x2E
#define CS18_SW2 0x2F
#define CS19_SW2 0x30
#define CS20_SW2 0x31
#define CS21_SW2 0x32
#define CS22_SW2 0x33
#define CS23_SW2 0x34
#define CS24_SW2 0x35
#define CS25_SW2 0x36
#define CS26_SW2 0x37
#define CS27_SW2 0x38
#define CS28_SW2 0x39
#define CS29_SW2 0x3A
#define CS30_SW2 0x3B
#define CS1_SW3 0x3C
#define CS2_SW3 0x3D
#define CS3_SW3 0x3E
#define CS4_SW3 0x3F
#define CS5_SW3 0x40
#define CS6_SW3 0x41
#define CS7_SW3 0x42
#define CS8_SW3 0x43
#define CS9_SW3 0x44
#define CS10_SW3 0x45
#define CS11_SW3 0x46
#define CS12_SW3 0x47
#define CS13_SW3 0x48
#define CS14_SW3 0x49
#define CS15_SW3 0x4A
#define CS16_SW3 0x4B
#define CS17_SW3 0x4C
#define CS18_SW3 0x4D
#define CS19_SW3 0x4E
#define CS20_SW3 0x4F
#define CS21_SW3 0x50
#define CS22_SW3 0x51
#define CS23_SW3 0x52
#define CS24_SW3 0x53
#define CS25_SW3 0x54
#define CS26_SW3 0x55
#define CS27_SW3 0x56
#define CS28_SW3 0x57
#define CS29_SW3 0x58
#define CS30_SW3 0x59
#define CS1_SW4 0x5A
#define CS2_SW4 0x5B
#define CS3_SW4 0x5C
#define CS4_SW4 0x5D
#define CS5_SW4 0x5E
#define CS6_SW4 0x5F
#define CS7_SW4 0x60
#define CS8_SW4 0x61
#define CS9_SW4 0x62
#define CS10_SW4 0x63
#define CS11_SW4 0x64
#define CS12_SW4 0x65
#define CS13_SW4 0x66
#define CS14_SW4 0x67
#define CS15_SW4 0x68
#define CS16_SW4 0x69
#define CS17_SW4 0x6A
#define CS18_SW4 0x6B
#define CS19_SW4 0x6C
#define CS20_SW4 0x6D
#define CS21_SW4 0x6E
#define CS22_SW4 0x6F
#define CS23_SW4 0x70
#define CS24_SW4 0x71
#define CS25_SW4 0x72
#define CS26_SW4 0x73
#define CS27_SW4 0x74
#define CS28_SW4 0x75
#define CS29_SW4 0x76
#define CS30_SW4 0x77
#define CS1_SW5 0x78
#define CS2_SW5 0x79
#define CS3_SW5 0x7A
#define CS4_SW5 0x7B
#define CS5_SW5 0x7C
#define CS6_SW5 0x7D
#define CS7_SW5 0x7E
#define CS8_SW5 0x7F
#define CS9_SW5 0x80
#define CS10_SW5 0x81
#define CS11_SW5 0x82
#define CS12_SW5 0x83
#define CS13_SW5 0x84
#define CS14_SW5 0x85
#define CS15_SW5 0x86
#define CS16_SW5 0x87
#define CS17_SW5 0x88
#define CS18_SW5 0x89
#define CS19_SW5 0x8A
#define CS20_SW5 0x8B
#define CS21_SW5 0x8C
#define CS22_SW5 0x8D
#define CS23_SW5 0x8E
#define CS24_SW5 0x8F
#define CS25_SW5 0x90
#define CS26_SW5 0x91
#define CS27_SW5 0x92
#define CS28_SW5 0x93
#define CS29_SW5 0x94
#define CS30_SW5 0x95
#define CS1_SW6 0x96
#define CS2_SW6 0x97
#define CS3_SW6 0x98
#define CS4_SW6 0x99
#define CS5_SW6 0x9A
#define CS6_SW6 0x9B
#define CS7_SW6 0x9C
#define CS8_SW6 0x9D
#define CS9_SW6 0x9E
#define CS10_SW6 0x9F
#define CS11_SW6 0xA0
#define CS12_SW6 0xA1
#define CS13_SW6 0xA2
#define CS14_SW6 0xA3
#define CS15_SW6 0xA4
#define CS16_SW6 0xA5
#define CS17_SW6 0xA6
#define CS18_SW6 0xA7
#define CS19_SW6 0xA8
#define CS20_SW6 0xA9
#define CS21_SW6 0xAA
#define CS22_SW6 0xAB
#define CS23_SW6 0xAC
#define CS24_SW6 0xAD
#define CS25_SW6 0xAE
#define CS26_SW6 0xAF
#define CS27_SW6 0xB0
#define CS28_SW6 0xB1
#define CS29_SW6 0xB2
#define CS30_SW6 0xB3
#define CS1_SW7 0xB4
#define CS2_SW7 0xB5
#define CS3_SW7 0xB6
#define CS4_SW7 0xB7
#define CS5_SW7 0xB8
#define CS6_SW7 0xB9
#define CS7_SW7 0xBA
#define CS8_SW7 0xBB
#define CS9_SW7 0xBC
#define CS10_SW7 0xBD
#define CS11_SW7 0xBE
#define CS12_SW7 0xBF
#define CS13_SW7 0xC0
#define CS14_SW7 0xC1
#define CS15_SW7 0xC2
#define CS16_SW7 0xC3
#define CS17_SW7 0xC4
#define CS18_SW7 0xC5
#define CS19_SW7 0xC6
#define CS20_SW7 0xC7
#define CS21_SW7 0xC8
#define CS22_SW7 0xC9
#define CS23_SW7 0xCA
#define CS24_SW7 0xCB
#define CS25_SW7 0xCC
#define CS26_SW7 0xCD
#define CS27_SW7 0xCE
#define CS28_SW7 0xCF
#define CS29_SW7 0xD0
#define CS30_SW7 0xD1
#define CS1_SW8 0xD2
#define CS2_SW8 0xD3
#define CS3_SW8 0xD4
#define CS4_SW8 0xD5
#define CS5_SW8 0xD6
#define CS6_SW8 0xD7
#define CS7_SW8 0xD8
#define CS8_SW8 0xD9
#define CS9_SW8 0xDA
#define CS10_SW8 0xDB
#define CS11_SW8 0xDC
#define CS12_SW8 0xDD
#define CS13_SW8 0xDE
#define CS14_SW8 0xDF
#define CS15_SW8 0xE0
#define CS16_SW8 0xE1
#define CS17_SW8 0xE2
#define CS18_SW8 0xE3
#define CS19_SW8 0xE4
#define CS20_SW8 0xE5
#define CS21_SW8 0xE6
#define CS22_SW8 0xE7
#define CS23_SW8 0xE8
#define CS24_SW8 0xE9
#define CS25_SW8 0xEA
#define CS26_SW8 0xEB
#define CS27_SW8 0xEC
#define CS28_SW8 0xED
#define CS29_SW8 0xEE
#define CS30_SW8 0xEF
#define CS1_SW9 0xF0
#define CS2_SW9 0xF1
#define CS3_SW9 0xF2
#define CS4_SW9 0xF3
#define CS5_SW9 0xF4
#define CS6_SW9 0xF5
#define CS7_SW9 0xF6
#define CS8_SW9 0xF7
#define CS9_SW9 0xF8
#define CS10_SW9 0xF9
#define CS11_SW9 0xFA
#define CS12_SW9 0xFB
#define CS13_SW9 0xFC
#define CS14_SW9 0xFD
#define CS15_SW9 0xFE
#define CS16_SW9 0xFF
#define CS17_SW9 0x100
#define CS18_SW9 0x101
#define CS19_SW9 0x102
#define CS20_SW9 0x103
#define CS21_SW9 0x104
#define CS22_SW9 0x105
#define CS23_SW9 0x106
#define CS24_SW9 0x107
#define CS25_SW9 0x108
#define CS26_SW9 0x109
#define CS27_SW9 0x10A
#define CS28_SW9 0x10B
#define CS29_SW9 0x10C
#define CS30_SW9 0x10D
#define CS31_SW1 0x10E
#define CS32_SW1 0x10F
#define CS33_SW1 0x110
#define CS34_SW1 0x111
#define CS35_SW1 0x112
#define CS36_SW1 0x113
#define CS37_SW1 0x114
#define CS38_SW1 0x115
#define CS39_SW1 0x116
#define CS31_SW2 0x117
#define CS32_SW2 0x118
#define CS33_SW2 0x119
#define CS34_SW2 0x11A
#define CS35_SW2 0x11B
#define CS36_SW2 0x11C
#define CS37_SW2 0x11D
#define CS38_SW2 0x11E
#define CS39_SW2 0x11F
#define CS31_SW3 0x120
#define CS32_SW3 0x121
#define CS33_SW3 0x122
#define CS34_SW3 0x123
#define CS35_SW3 0x124
#define CS36_SW3 0x125
#define CS37_SW3 0x126
#define CS38_SW3 0x127
#define CS39_SW3 0x128
#define CS31_SW4 0x129
#define CS32_SW4 0x12A
#define CS33_SW4 0x12B
#define CS34_SW4 0x12C
#define CS35_SW4 0x12D
#define CS36_SW4 0x12E
#define CS37_SW4 0x12F
#define CS38_SW4 0x130
#define CS39_SW4 0x131
#define CS31_SW5 0x132
#define CS32_SW5 0x133
#define CS33_SW5 0x134
#define CS34_SW5 0x135
#define CS35_SW5 0x136
#define CS36_SW5 0x137
#define CS37_SW5 0x138
#define CS38_SW5 0x139
#define CS39_SW5 0x13A
#define CS31_SW6 0x13B
#define CS32_SW6 0x13C
#define CS33_SW6 0x13D
#define CS34_SW6 0x13E
#define CS35_SW6 0x13F
#define CS36_SW6 0x140
#define CS37_SW6 0x141
#define CS38_SW6 0x142
#define CS39_SW6 0x143
#define CS31_SW7 0x144
#define CS32_SW7 0x145
#define CS33_SW7 0x146
#define CS34_SW7 0x147
#define CS35_SW7 0x148
#define CS36_SW7 0x149
#define CS37_SW7 0x14A
#define CS38_SW7 0x14B
#define CS39_SW7 0x14C
#define CS31_SW8 0x14D
#define CS32_SW8 0x14E
#define CS33_SW8 0x14F
#define CS34_SW8 0x150
#define CS35_SW8 0x151
#define CS36_SW8 0x152
#define CS37_SW8 0x153
#define CS38_SW8 0x154
#define CS39_SW8 0x155
#define CS31_SW9 0x156
#define CS32_SW9 0x157
#define CS33_SW9 0x158
#define CS34_SW9 0x159
#define CS35_SW9 0x15A
#define CS36_SW9 0x15B
#define CS37_SW9 0x15C
#define CS38_SW9 0x15D
#define CS39_SW9 0x15E

View File

@ -17,63 +17,58 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "wait.h"
#include "is31fl3741.h"
#include <string.h>
#include "i2c_master.h"
#include "progmem.h"
#include "wait.h"
// This is a 7-bit address, that gets left-shifted and bit 0
// set to 0 for write, 1 for read (as per I2C protocol)
// The address will vary depending on your wiring:
// 00 <-> GND
// 01 <-> SCL
// 10 <-> SDA
// 11 <-> VCC
// ADDR1 represents A1:A0 of the 7-bit address.
// ADDR2 represents A3:A2 of the 7-bit address.
// The result is: 0b101(ADDR2)(ADDR1)
#define ISSI_ADDR_DEFAULT 0x60
#define IS31FL3741_COMMANDREGISTER 0xFD
#define IS31FL3741_COMMANDREGISTER_WRITELOCK 0xFE
#define IS31FL3741_INTERRUPTMASKREGISTER 0xF0
#define IS31FL3741_INTERRUPTSTATUSREGISTER 0xF1
#define IS31FL3741_IDREGISTER 0xFC
#define ISSI_COMMANDREGISTER 0xFD
#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE
#define ISSI_INTERRUPTMASKREGISTER 0xF0
#define ISSI_INTERRUPTSTATUSREGISTER 0xF1
#define ISSI_IDREGISTER 0xFC
#define IS31FL3741_PAGE_PWM0 0x00 // PG0
#define IS31FL3741_PAGE_PWM1 0x01 // PG1
#define IS31FL3741_PAGE_SCALING_0 0x02 // PG2
#define IS31FL3741_PAGE_SCALING_1 0x03 // PG3
#define IS31FL3741_PAGE_FUNCTION 0x04 // PG4
#define ISSI_PAGE_PWM0 0x00 // PG0
#define ISSI_PAGE_PWM1 0x01 // PG1
#define ISSI_PAGE_SCALING_0 0x02 // PG2
#define ISSI_PAGE_SCALING_1 0x03 // PG3
#define ISSI_PAGE_FUNCTION 0x04 // PG4
#define IS31FL3741_REG_CONFIGURATION 0x00 // PG4
#define IS31FL3741_REG_GLOBALCURRENT 0x01 // PG4
#define IS31FL3741_REG_PULLDOWNUP 0x02 // PG4
#define IS31FL3741_REG_PWM_FREQUENCY 0x36 // PG4
#define IS31FL3741_REG_RESET 0x3F // PG4
#define ISSI_REG_CONFIGURATION 0x00 // PG4
#define ISSI_REG_GLOBALCURRENT 0x01 // PG4
#define ISSI_REG_PULLDOWNUP 0x02 // PG4
#define ISSI_REG_RESET 0x3F // PG4
#ifndef ISSI_TIMEOUT
# define ISSI_TIMEOUT 100
#ifndef IS31FL3741_I2C_TIMEOUT
# define IS31FL3741_I2C_TIMEOUT 100
#endif
#ifndef ISSI_PERSISTENCE
# define ISSI_PERSISTENCE 0
#ifndef IS31FL3741_I2C_PERSISTENCE
# define IS31FL3741_I2C_PERSISTENCE 0
#endif
#ifndef ISSI_SWPULLUP
# define ISSI_SWPULLUP PUR_32KR
#ifndef IS31FL3741_CONFIGURATION
# define IS31FL3741_CONFIGURATION 0x01
#endif
#ifndef ISSI_CSPULLUP
# define ISSI_CSPULLUP PUR_32KR
#ifndef IS31FL3741_PWM_FREQUENCY
# define IS31FL3741_PWM_FREQUENCY IS31FL3741_PWM_FREQUENCY_29K_HZ
#endif
#ifndef ISSI_GLOBALCURRENT
# define ISSI_GLOBALCURRENT 0xFF
#ifndef IS31FL3741_SWPULLUP
# define IS31FL3741_SWPULLUP IS31FL3741_PUR_32KR
#endif
#define ISSI_MAX_LEDS 351
#ifndef IS31FL3741_CSPULLUP
# define IS31FL3741_CSPULLUP IS31FL3741_PUR_32KR
#endif
#ifndef IS31FL3741_GLOBALCURRENT
# define IS31FL3741_GLOBALCURRENT 0xFF
#endif
#define IS31FL3741_MAX_LEDS 351
// Transfer buffer for TWITransmitData()
uint8_t g_twi_transfer_buffer[20] = {0xFF};
@ -84,22 +79,22 @@ uint8_t g_twi_transfer_buffer[20] = {0xFF};
// We could optimize this and take out the unused registers from these
// 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};
bool g_scaling_registers_update_required[DRIVER_COUNT] = {false};
uint8_t g_pwm_buffer[IS31FL3741_DRIVER_COUNT][IS31FL3741_MAX_LEDS];
bool g_pwm_buffer_update_required[IS31FL3741_DRIVER_COUNT] = {false};
bool g_scaling_registers_update_required[IS31FL3741_DRIVER_COUNT] = {false};
uint8_t g_scaling_registers[DRIVER_COUNT][ISSI_MAX_LEDS];
uint8_t g_scaling_registers[IS31FL3741_DRIVER_COUNT][IS31FL3741_MAX_LEDS];
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;
#if ISSI_PERSISTENCE > 0
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) == 0) break;
#if IS31FL3741_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3741_I2C_TIMEOUT) == 0) break;
}
#else
i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT);
i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, IS31FL3741_I2C_TIMEOUT);
#endif
}
@ -109,21 +104,21 @@ bool is31fl3741_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
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, IS31FL3741_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3741_write_register(addr, IS31FL3741_COMMANDREGISTER, IS31FL3741_PAGE_PWM1);
}
g_twi_transfer_buffer[0] = i % 180;
memcpy(g_twi_transfer_buffer + 1, pwm_buffer + i, 18);
#if ISSI_PERSISTENCE > 0
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 19, ISSI_TIMEOUT) != 0) {
#if IS31FL3741_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 19, IS31FL3741_I2C_TIMEOUT) != 0) {
return false;
}
}
#else
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 19, ISSI_TIMEOUT) != 0) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 19, IS31FL3741_I2C_TIMEOUT) != 0) {
return false;
}
#endif
@ -133,14 +128,14 @@ bool is31fl3741_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer) {
g_twi_transfer_buffer[0] = 162;
memcpy(g_twi_transfer_buffer + 1, pwm_buffer + 342, 9);
#if ISSI_PERSISTENCE > 0
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 10, ISSI_TIMEOUT) != 0) {
#if IS31FL3741_I2C_PERSISTENCE > 0
for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 10, IS31FL3741_I2C_TIMEOUT) != 0) {
return false;
}
}
#else
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 10, ISSI_TIMEOUT) != 0) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 10, IS31FL3741_I2C_TIMEOUT) != 0) {
return false;
}
#endif
@ -156,18 +151,20 @@ 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, IS31FL3741_COMMANDREGISTER_WRITELOCK, 0xC5);
// Select PG4
is31fl3741_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION);
is31fl3741_write_register(addr, IS31FL3741_COMMANDREGISTER, IS31FL3741_PAGE_FUNCTION);
// Set to Normal operation
is31fl3741_write_register(addr, ISSI_REG_CONFIGURATION, 0x01);
is31fl3741_write_register(addr, IS31FL3741_REG_CONFIGURATION, IS31FL3741_CONFIGURATION);
// Set Golbal Current Control Register
is31fl3741_write_register(addr, ISSI_REG_GLOBALCURRENT, ISSI_GLOBALCURRENT);
is31fl3741_write_register(addr, IS31FL3741_REG_GLOBALCURRENT, IS31FL3741_GLOBALCURRENT);
// Set Pull up & Down for SWx CSy
is31fl3741_write_register(addr, ISSI_REG_PULLDOWNUP, ((ISSI_CSPULLUP << 4) | ISSI_SWPULLUP));
is31fl3741_write_register(addr, IS31FL3741_REG_PULLDOWNUP, ((IS31FL3741_CSPULLUP << 4) | IS31FL3741_SWPULLUP));
// Set PWM frequency
is31fl3741_write_register(addr, IS31FL3741_REG_PWM_FREQUENCY, (IS31FL3741_PWM_FREQUENCY & 0b1111));
// is31fl3741_update_led_scaling_registers(addr, 0xFF, 0xFF, 0xFF);
@ -176,9 +173,9 @@ void is31fl3741_init(uint8_t addr) {
}
void is31fl3741_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
is31_led led;
is31fl3741_led_t led;
if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
memcpy_P(&led, (&g_is31fl3741_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;
@ -197,8 +194,8 @@ 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) {
is31_led led;
memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));
is31fl3741_led_t led;
memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led));
if (red) {
g_scaling_registers[led.driver][led.r] = 0xFF;
@ -224,8 +221,8 @@ void is31fl3741_set_led_control_register(uint8_t index, bool red, bool green, bo
void is31fl3741_update_pwm_buffers(uint8_t addr, uint8_t index) {
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, IS31FL3741_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3741_write_register(addr, IS31FL3741_COMMANDREGISTER, IS31FL3741_PAGE_PWM0);
is31fl3741_write_pwm_buffer(addr, g_pwm_buffer[index]);
}
@ -233,7 +230,7 @@ void is31fl3741_update_pwm_buffers(uint8_t addr, uint8_t 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 is31fl3741_led_t *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;
@ -244,8 +241,8 @@ void is31fl3741_set_pwm_buffer(const is31_led *pled, uint8_t red, uint8_t green,
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, IS31FL3741_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3741_write_register(addr, IS31FL3741_COMMANDREGISTER, IS31FL3741_PAGE_SCALING_0);
// CS1_SW1 to CS30_SW6 are on PG2
for (int i = CS1_SW1; i <= CS30_SW6; ++i) {
@ -253,8 +250,8 @@ void is31fl3741_update_led_control_registers(uint8_t addr, uint8_t index) {
}
// 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, IS31FL3741_COMMANDREGISTER_WRITELOCK, 0xC5);
is31fl3741_write_register(addr, IS31FL3741_COMMANDREGISTER, IS31FL3741_PAGE_SCALING_1);
// CS1_SW7 to CS39_SW9 are on PG3
for (int i = CS1_SW7; i <= CS39_SW9; ++i) {
@ -265,7 +262,7 @@ 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_scaling_registers(const is31fl3741_led_t *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;

View File

@ -23,14 +23,55 @@
#include <stdbool.h>
#include "progmem.h"
typedef struct is31_led {
// ======== DEPRECATED DEFINES - DO NOT USE ========
#ifdef DRIVER_COUNT
# define IS31FL3741_DRIVER_COUNT DRIVER_COUNT
#endif
#ifdef ISSI_TIMEOUT
# define IS31FL3741_I2C_TIMEOUT ISSI_TIMEOUT
#endif
#ifdef ISSI_PERSISTENCE
# define IS31FL3741_I2C_PERSISTENCE ISSI_PERSISTENCE
#endif
#ifdef ISSI_CONFIGURATION
# define IS31FL3741_CONFIGURATION ISSI_CONFIGURATION
#endif
#ifdef ISSI_SWPULLUP
# define IS31FL3741_SWPULLUP ISSI_SWPULLUP
#endif
#ifdef ISSI_CSPULLUP
# define IS31FL3741_CSPULLUP ISSI_CSPULLUP
#endif
#ifdef ISSI_GLOBALCURRENT
# define IS31FL3741_GLOBALCURRENT ISSI_GLOBALCURRENT
#endif
#define is31_led is31fl3741_led_t
#define g_is31_leds g_is31fl3741_leds
#define PUR_0R IS31FL3741_PUR_0R
#define PUR_05KR IS31FL3741_PUR_05KR
#define PUR_1KR IS31FL3741_PUR_1KR
#define PUR_2KR IS31FL3741_PUR_2KR
#define PUR_4KR IS31FL3741_PUR_4KR
#define PUR_8KR IS31FL3741_PUR_8KR
#define PUR_16KR IS31FL3741_PUR_16KR
#define PUR_32KR IS31FL3741_PUR_32KR
// ========
#define IS31FL3741_I2C_ADDRESS_GND 0x30
#define IS31FL3741_I2C_ADDRESS_SCL 0x31
#define IS31FL3741_I2C_ADDRESS_SDA 0x32
#define IS31FL3741_I2C_ADDRESS_VCC 0x33
typedef struct is31fl3741_led_t {
uint32_t driver : 2;
uint32_t r : 10;
uint32_t g : 10;
uint32_t b : 10;
} __attribute__((packed)) is31_led;
} __attribute__((packed)) is31fl3741_led_t;
extern const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT];
extern const is31fl3741_led_t PROGMEM g_is31fl3741_leds[RGB_MATRIX_LED_COUNT];
void is31fl3741_init(uint8_t addr);
void is31fl3741_write_register(uint8_t addr, uint8_t reg, uint8_t data);
@ -47,18 +88,23 @@ void is31fl3741_set_led_control_register(uint8_t index, bool red, bool green, bo
// 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_set_scaling_registers(const is31fl3741_led_t *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 is31fl3741_led_t *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
#define PUR_1KR 0x02 // 1.0k Ohm resistor
#define PUR_2KR 0x03 // 2.0k Ohm resistor
#define PUR_4KR 0x04 // 4.0k Ohm resistor
#define PUR_8KR 0x05 // 8.0k Ohm resistor
#define PUR_16KR 0x06 // 16k Ohm resistor
#define PUR_32KR 0x07 // 32k Ohm resistor
#define IS31FL3741_PUR_0R 0x00 // No PUR resistor
#define IS31FL3741_PUR_05KR 0x01 // 0.5k Ohm resistor
#define IS31FL3741_PUR_1KR 0x02 // 1.0k Ohm resistor
#define IS31FL3741_PUR_2KR 0x03 // 2.0k Ohm resistor
#define IS31FL3741_PUR_4KR 0x04 // 4.0k Ohm resistor
#define IS31FL3741_PUR_8KR 0x05 // 8.0k Ohm resistor
#define IS31FL3741_PUR_16KR 0x06 // 16k Ohm resistor
#define IS31FL3741_PUR_32KR 0x07 // 32k Ohm resistor
#define IS31FL3741_PWM_FREQUENCY_29K_HZ 0b0000
#define IS31FL3741_PWM_FREQUENCY_3K6_HZ 0b0011
#define IS31FL3741_PWM_FREQUENCY_1K8_HZ 0b0111
#define IS31FL3741_PWM_FREQUENCY_900_HZ 0b1011
#define CS1_SW1 0x00
#define CS2_SW1 0x01

Some files were not shown because too many files have changed in this diff Show More