mirror of
https://github.com/qmk/qmk_firmware.git
synced 2025-09-10 17:15:43 +00:00
Compare commits
404 Commits
0.22.6
...
more_dd_ee
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
668c29ca52 | ||
|
|
bf6f13a2b0 | ||
|
|
79094d3f72 | ||
|
|
cf7d3435d7 | ||
|
|
1e9b299fb0 | ||
|
|
4410e6b3bf | ||
|
|
8e614250b4 | ||
|
|
48d9140cfc | ||
|
|
d56ee70c52 | ||
|
|
3f1b3a5125 | ||
|
|
87b4bc17ed | ||
|
|
0e6d47e1e4 | ||
|
|
2b8d9f3193 | ||
|
|
e4c54a9612 | ||
|
|
20cefe254d | ||
|
|
f6c70c40af | ||
|
|
184c5844be | ||
|
|
81a3aa025c | ||
|
|
1bff37781b | ||
|
|
1da7c8c8d0 | ||
|
|
23a8f7f03b | ||
|
|
fd18d95b77 | ||
|
|
a46c7abd01 | ||
|
|
539c8e3b72 | ||
|
|
527a4ee846 | ||
|
|
62ff02a101 | ||
|
|
0943d0a2a0 | ||
|
|
032b039d0b | ||
|
|
c157c19b81 | ||
|
|
f150258e5a | ||
|
|
0d7d2f7915 | ||
|
|
0477160109 | ||
|
|
d0c495f4bc | ||
|
|
e22cb99bc4 | ||
|
|
ed78c13b5f | ||
|
|
daf540c0bb | ||
|
|
391c0428c9 | ||
|
|
a516f20215 | ||
|
|
c67c9f1d98 | ||
|
|
f4ed65169f | ||
|
|
b630df1aa7 | ||
|
|
04b8159d17 | ||
|
|
999e721312 | ||
|
|
28696197e5 | ||
|
|
b5423b2e83 | ||
|
|
a682a98ef3 | ||
|
|
f536646235 | ||
|
|
007c0c1db4 | ||
|
|
c72d661b93 | ||
|
|
ad4ecd6eb4 | ||
|
|
5e8a156222 | ||
|
|
fa8940e5a6 | ||
|
|
3c0f9599b7 | ||
|
|
527790b000 | ||
|
|
ffcc54833d | ||
|
|
e03afcb083 | ||
|
|
db8b74f016 | ||
|
|
47145a6ce8 | ||
|
|
81e086b387 | ||
|
|
474a405606 | ||
|
|
0650f783b5 | ||
|
|
17c7d1fac3 | ||
|
|
6dafcac713 | ||
|
|
0e5f7ac3ba | ||
|
|
81d69a5798 | ||
|
|
509a2b40b7 | ||
|
|
77fe1298c7 | ||
|
|
20eb93e05c | ||
|
|
683cdf3a43 | ||
|
|
3665ae0441 | ||
|
|
fa1c1cbbee | ||
|
|
e2e6b3dfd6 | ||
|
|
6e6fff866e | ||
|
|
62731fe255 | ||
|
|
ad14a27cd3 | ||
|
|
21f15960a9 | ||
|
|
e1630edef4 | ||
|
|
528ebb42a4 | ||
|
|
4345d34cf2 | ||
|
|
690792eca9 | ||
|
|
7ca652ce6d | ||
|
|
99b545843e | ||
|
|
21389fbd89 | ||
|
|
ab952c358e | ||
|
|
06d123de62 | ||
|
|
48b694b8d6 | ||
|
|
b768d74b2b | ||
|
|
6c858477f1 | ||
|
|
69c1790dce | ||
|
|
6c3a512923 | ||
|
|
63b48f5096 | ||
|
|
0ca60eb759 | ||
|
|
127560ae22 | ||
|
|
a7406a429e | ||
|
|
5f33a251b3 | ||
|
|
70f0c279f9 | ||
|
|
92873cc14f | ||
|
|
2a8b3a809d | ||
|
|
d30fe46b51 | ||
|
|
8f757eece8 | ||
|
|
4e314d75af | ||
|
|
aaebb0a231 | ||
|
|
c33354805c | ||
|
|
9f31783249 | ||
|
|
d187a0a3af | ||
|
|
61a72b9c20 | ||
|
|
a58f86812c | ||
|
|
cf5077451f | ||
|
|
22fbbbb1cc | ||
|
|
c05603fa8c | ||
|
|
7603a464cd | ||
|
|
b5cbd7f040 | ||
|
|
d33f9ec86b | ||
|
|
a0805eda18 | ||
|
|
6e93b59f61 | ||
|
|
d99dbe4d56 | ||
|
|
c0f16be50f | ||
|
|
24a795d7a9 | ||
|
|
5485d5023e | ||
|
|
2ba0a75c2f | ||
|
|
38f89dfc2f | ||
|
|
6f59236b1f | ||
|
|
75ba2db2c1 | ||
|
|
177fee0ad4 | ||
|
|
d9fa80c0b0 | ||
|
|
733f1da96b | ||
|
|
a638f6e5aa | ||
|
|
3df155f203 | ||
|
|
d6b16b0df0 | ||
|
|
bc5d407bf7 | ||
|
|
800ea2af89 | ||
|
|
f33881b1da | ||
|
|
bd5860de4e | ||
|
|
6e3f770d0d | ||
|
|
997a6747ee | ||
|
|
e78fe5f77b | ||
|
|
1b7a538353 | ||
|
|
da5d745c73 | ||
|
|
8813bdf5dd | ||
|
|
ca2cb4f28c | ||
|
|
c23d873e1a | ||
|
|
07ec38f345 | ||
|
|
b404d5e9e4 | ||
|
|
6910ed2de5 | ||
|
|
2eed1f5bad | ||
|
|
67f52935e3 | ||
|
|
cfb4bb9b98 | ||
|
|
cfc668d43d | ||
|
|
ab1e851e7d | ||
|
|
931354ca35 | ||
|
|
5626a9282c | ||
|
|
7b1c200380 | ||
|
|
daea202bfd | ||
|
|
79464f3c52 | ||
|
|
f214ee7f3a | ||
|
|
943876c432 | ||
|
|
0edc68f7cd | ||
|
|
a268437f17 | ||
|
|
c5706ef791 | ||
|
|
4a855bd2d1 | ||
|
|
346b06d391 | ||
|
|
f4677c866e | ||
|
|
fb0c64a567 | ||
|
|
da9f894341 | ||
|
|
d58f85e09e | ||
|
|
288c2313fe | ||
|
|
18bc541694 | ||
|
|
41977a6010 | ||
|
|
0ecb03ad47 | ||
|
|
4e86dca49d | ||
|
|
32de27bb20 | ||
|
|
5da3604ec3 | ||
|
|
25c850e762 | ||
|
|
351b5cd2cd | ||
|
|
80ccbdfd86 | ||
|
|
d202355f75 | ||
|
|
ca9664b1ac | ||
|
|
be1214814e | ||
|
|
46c1e7351e | ||
|
|
17ae28f0e4 | ||
|
|
fc25b92e0f | ||
|
|
8d9c770a81 | ||
|
|
1acecc38e9 | ||
|
|
0b6c9385bc | ||
|
|
6eb2e43a8d | ||
|
|
339bff6339 | ||
|
|
1052c03a5b | ||
|
|
2fad45132f | ||
|
|
a7afa58fb6 | ||
|
|
d3b82322af | ||
|
|
538978b782 | ||
|
|
a931e6d7bd | ||
|
|
9b0887359b | ||
|
|
2563c9ba7f | ||
|
|
7e923580a5 | ||
|
|
234e099c72 | ||
|
|
21ed9b4575 | ||
|
|
61702b2564 | ||
|
|
a4760c4d1b | ||
|
|
c597731d1d | ||
|
|
6b671f93c6 | ||
|
|
0c4bd1ccdc | ||
|
|
4d994458f0 | ||
|
|
27ad5b4f4e | ||
|
|
c10a46aa43 | ||
|
|
9d526b8d66 | ||
|
|
e3cbd8a965 | ||
|
|
2181da4b72 | ||
|
|
de8d8686d2 | ||
|
|
1a1085da48 | ||
|
|
408d61dcfb | ||
|
|
e0eb90aba1 | ||
|
|
f35dee5c05 | ||
|
|
960d6e0d7d | ||
|
|
dd94877ec6 | ||
|
|
1fb02d5ad8 | ||
|
|
131bc92b77 | ||
|
|
4ebb065f94 | ||
|
|
a360900fbb | ||
|
|
4e7e824a73 | ||
|
|
9d3c0018c1 | ||
|
|
fb18ac2b10 | ||
|
|
2f4e1a78ad | ||
|
|
1d94de5358 | ||
|
|
7850f0d8c6 | ||
|
|
246f3cb4d2 | ||
|
|
99290b4c7e | ||
|
|
7a761ebf7d | ||
|
|
2821561618 | ||
|
|
a69262398f | ||
|
|
b667408be3 | ||
|
|
2f7b7a1273 | ||
|
|
59787a2e51 | ||
|
|
6424dadd37 | ||
|
|
9c340077f6 | ||
|
|
c9810facce | ||
|
|
5271e89fa7 | ||
|
|
81ea182be4 | ||
|
|
5024835c6f | ||
|
|
4873d8c27d | ||
|
|
4f00ca8d51 | ||
|
|
389c8eea85 | ||
|
|
ac3a1c3569 | ||
|
|
96bb79fd75 | ||
|
|
d5baa925e7 | ||
|
|
c08cf78b47 | ||
|
|
8ea8b80b85 | ||
|
|
adc217137e | ||
|
|
ad7a405fa5 | ||
|
|
f8cea06be3 | ||
|
|
3062c98093 | ||
|
|
629ac9ae68 | ||
|
|
4df24bc05d | ||
|
|
298fa76d1e | ||
|
|
139a7fc6c2 | ||
|
|
656b931d70 | ||
|
|
966a560836 | ||
|
|
53e543386e | ||
|
|
d820114560 | ||
|
|
b57eb73fcd | ||
|
|
9edeec7f23 | ||
|
|
bb50721ddd | ||
|
|
db68a95cae | ||
|
|
6b5cb01834 | ||
|
|
bc9bb2f3be | ||
|
|
29ffe804b8 | ||
|
|
b6a881ba32 | ||
|
|
21c396a3aa | ||
|
|
9a4c307740 | ||
|
|
4b26d9b62b | ||
|
|
3575e27ece | ||
|
|
f03c470adf | ||
|
|
c2c7b7af7a | ||
|
|
2aacda3235 | ||
|
|
7bfd775a5c | ||
|
|
f7f775a4ef | ||
|
|
16222f53a3 | ||
|
|
0c6c51eb63 | ||
|
|
8f22105289 | ||
|
|
74360d0a01 | ||
|
|
c7972f2645 | ||
|
|
8f9847248a | ||
|
|
db6b97e90b | ||
|
|
7654549bff | ||
|
|
a32c4ae918 | ||
|
|
a779e82bc7 | ||
|
|
25cf8546fe | ||
|
|
b3c397ba08 | ||
|
|
e0b3b12253 | ||
|
|
6e98c80eaa | ||
|
|
f457f0a26d | ||
|
|
78fdebb130 | ||
|
|
1ea54a2d8d | ||
|
|
32126dc4f0 | ||
|
|
f956ed7366 | ||
|
|
615ca78567 | ||
|
|
169b0f33f6 | ||
|
|
451ac2aa1e | ||
|
|
e8c6d1c620 | ||
|
|
f4a10933ca | ||
|
|
21c5d2de39 | ||
|
|
241415ecb6 | ||
|
|
85c2b03507 | ||
|
|
134b60bb25 | ||
|
|
35be48f525 | ||
|
|
6840589ca5 | ||
|
|
f0e2dd4b5e | ||
|
|
8e3a2b9755 | ||
|
|
8ee21ac16d | ||
|
|
16ca3121d2 | ||
|
|
24a922b54b | ||
|
|
f9777dfda4 | ||
|
|
fe00bb9746 | ||
|
|
97464e470c | ||
|
|
f739fa19e7 | ||
|
|
43ae1370f3 | ||
|
|
4e4ef11cf1 | ||
|
|
2b50cd7989 | ||
|
|
9c2c96a20e | ||
|
|
99a225e1a2 | ||
|
|
98f0c0895b | ||
|
|
d8e100ae3c | ||
|
|
b9d6bfe927 | ||
|
|
76daf29ef0 | ||
|
|
f9ed1e3ebc | ||
|
|
20cbe372a6 | ||
|
|
794afbd1c1 | ||
|
|
7d81d333f2 | ||
|
|
7c4d3d13e3 | ||
|
|
00feff656f | ||
|
|
36b4e290c8 | ||
|
|
5a69d3006a | ||
|
|
1b637287a1 | ||
|
|
8136eda6d4 | ||
|
|
1cbb5ae99e | ||
|
|
2d41443e6a | ||
|
|
d36f73a431 | ||
|
|
4a0badfb34 | ||
|
|
35aceab1a4 | ||
|
|
eb2db05e8a | ||
|
|
a0aebe5cd5 | ||
|
|
b9f2121d10 | ||
|
|
12b3dd36f3 | ||
|
|
de381ad3b7 | ||
|
|
920a8f658c | ||
|
|
3cb09bed1f | ||
|
|
7b313dbc89 | ||
|
|
8975dc6c3b | ||
|
|
6adf461aa0 | ||
|
|
bd2e59af60 | ||
|
|
909054c357 | ||
|
|
06cec720dd | ||
|
|
ad4fc42e6b | ||
|
|
d046f73b49 | ||
|
|
51d9520be7 | ||
|
|
5d61bc7eb1 | ||
|
|
a4f28b9d1f | ||
|
|
ddb737e716 | ||
|
|
4a51f06c83 | ||
|
|
03b7d8217f | ||
|
|
9d4347b699 | ||
|
|
32c90bf026 | ||
|
|
3aebc8acb0 | ||
|
|
5633a2cdb4 | ||
|
|
4413805a64 | ||
|
|
3472536796 | ||
|
|
67c366850e | ||
|
|
092fa3b61a | ||
|
|
8600f5b48a | ||
|
|
5fab310e65 | ||
|
|
a03de8440b | ||
|
|
ae14710a10 | ||
|
|
ecec4e4a7a | ||
|
|
caf6245c32 | ||
|
|
aec7f29df7 | ||
|
|
a40cccced2 | ||
|
|
41bd4e35a0 | ||
|
|
1e3095f9cc | ||
|
|
1b42d2ccfd | ||
|
|
b87877660e | ||
|
|
dd887eae36 | ||
|
|
911c715f49 | ||
|
|
23bd6f16f9 | ||
|
|
5c16bb4fc4 | ||
|
|
e149340351 | ||
|
|
a74647c1fa | ||
|
|
c6b979b162 | ||
|
|
8d8775aff2 | ||
|
|
413c3d349c | ||
|
|
ba8de0ef74 | ||
|
|
faa7c1a1d8 | ||
|
|
b0f5eb8f00 | ||
|
|
367b8e09d9 | ||
|
|
2447765181 | ||
|
|
5aaf5a8c25 | ||
|
|
b078d0bfc5 | ||
|
|
7e8a64ca53 | ||
|
|
d78c0007ba | ||
|
|
e798c02d82 | ||
|
|
14295563e1 | ||
|
|
5dd1a68cb6 | ||
|
|
66fcc7f5f5 | ||
|
|
42e369dc9e | ||
|
|
9055dbbe65 |
2
.github/workflows/format.yml
vendored
2
.github/workflows/format.yml
vendored
@@ -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}'
|
||||
|
||||
27
.github/workflows/lint.yml
vendored
27
.github/workflows/lint.yml
vendored
@@ -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
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -108,5 +108,6 @@ compile_commands.json
|
||||
.clangd/
|
||||
.cache/
|
||||
|
||||
# VIA(L) json files that don't belong in QMK repo
|
||||
# VIA(L) files that don't belong in QMK repo
|
||||
via*.json
|
||||
/keyboards/**/keymaps/vial/*
|
||||
|
||||
26
Makefile
26
Makefile
@@ -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 x$$(TEST_NAME)x, x$$(notdir $$(TEST))x), $$(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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -450,6 +455,7 @@ $(eval $(call add_qmk_prefix_defs,MCU_PORT_NAME,MCU_PORT_NAME))
|
||||
$(eval $(call add_qmk_prefix_defs,MCU_FAMILY,MCU_FAMILY))
|
||||
$(eval $(call add_qmk_prefix_defs,MCU_SERIES,MCU_SERIES))
|
||||
$(eval $(call add_qmk_prefix_defs,BOARD,BOARD))
|
||||
$(eval $(call add_qmk_prefix_defs,OPT,OPT))
|
||||
|
||||
# Control whether intermediate file listings are generated
|
||||
# e.g.:
|
||||
@@ -495,10 +501,10 @@ check-size: top-symbols
|
||||
top-symbols: build
|
||||
echo "###########################################"
|
||||
echo "# Highest flash usage:"
|
||||
$(NM) -Crtd --size-sort $(BUILD_DIR)/$(TARGET).elf | grep -i ' [t] ' | head -n$(NUM_TOP_SYMBOLS) | sed -e 's#^0000000# #g' -e 's#^000000# #g' -e 's#^00000# #g' -e 's#^0000# #g' -e 's#^000# #g' -e 's#^00# #g' -e 's#^0# #g'
|
||||
$(NM) -Crtd --size-sort $(BUILD_DIR)/$(TARGET).elf | grep ' [RrTt] ' | head -n$(NUM_TOP_SYMBOLS) | sed -e 's#^0000000# #g' -e 's#^000000# #g' -e 's#^00000# #g' -e 's#^0000# #g' -e 's#^000# #g' -e 's#^00# #g' -e 's#^0# #g'
|
||||
echo "###########################################"
|
||||
echo "# Highest RAM usage:"
|
||||
$(NM) -Crtd --size-sort $(BUILD_DIR)/$(TARGET).elf | grep -i ' [dbv] ' | head -n$(NUM_TOP_SYMBOLS) | sed -e 's#^0000000# #g' -e 's#^000000# #g' -e 's#^00000# #g' -e 's#^0000# #g' -e 's#^000# #g' -e 's#^00# #g' -e 's#^0# #g'
|
||||
$(NM) -Crtd --size-sort $(BUILD_DIR)/$(TARGET).elf | grep ' [BbCDdGgSs] ' | head -n$(NUM_TOP_SYMBOLS) | sed -e 's#^0000000# #g' -e 's#^000000# #g' -e 's#^00000# #g' -e 's#^0000# #g' -e 's#^000# #g' -e 's#^00# #g' -e 's#^0# #g'
|
||||
echo "###########################################"
|
||||
endif
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -151,30 +151,25 @@ ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes)
|
||||
endif
|
||||
OPT_DEFS += -DPOINTING_DEVICE_DRIVER_$(strip $(POINTING_DEVICE_DRIVER))
|
||||
ifeq ($(strip $(POINTING_DEVICE_DRIVER)), adns9800)
|
||||
OPT_DEFS += -DSTM32_SPI -DHAL_USE_SPI=TRUE
|
||||
QUANTUM_LIB_SRC += spi_master.c
|
||||
SPI_DRIVER_REQUIRED = yes
|
||||
else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), analog_joystick)
|
||||
OPT_DEFS += -DSTM32_ADC -DHAL_USE_ADC=TRUE
|
||||
LIB_SRC += analog.c
|
||||
else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), cirque_pinnacle_i2c)
|
||||
OPT_DEFS += -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
SRC += drivers/sensors/cirque_pinnacle.c
|
||||
SRC += drivers/sensors/cirque_pinnacle_gestures.c
|
||||
SRC += $(QUANTUM_DIR)/pointing_device/pointing_device_gestures.c
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), cirque_pinnacle_spi)
|
||||
OPT_DEFS += -DSTM32_SPI -DHAL_USE_SPI=TRUE
|
||||
SPI_DRIVER_REQUIRED = yes
|
||||
SRC += drivers/sensors/cirque_pinnacle.c
|
||||
SRC += drivers/sensors/cirque_pinnacle_gestures.c
|
||||
SRC += $(QUANTUM_DIR)/pointing_device/pointing_device_gestures.c
|
||||
QUANTUM_LIB_SRC += spi_master.c
|
||||
else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), pimoroni_trackball)
|
||||
OPT_DEFS += -DSTM32_SPI -DHAL_USE_I2C=TRUE
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
else ifneq ($(filter $(strip $(POINTING_DEVICE_DRIVER)),pmw3360 pmw3389),)
|
||||
OPT_DEFS += -DSTM32_SPI -DHAL_USE_SPI=TRUE
|
||||
SPI_DRIVER_REQUIRED = yes
|
||||
SRC += drivers/sensors/pmw33xx_common.c
|
||||
QUANTUM_LIB_SRC += spi_master.c
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@@ -204,12 +199,12 @@ else
|
||||
else ifeq ($(strip $(EEPROM_DRIVER)), i2c)
|
||||
# External I2C EEPROM implementation
|
||||
OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_I2C
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
SRC += eeprom_driver.c eeprom_i2c.c
|
||||
else ifeq ($(strip $(EEPROM_DRIVER)), spi)
|
||||
# External SPI EEPROM implementation
|
||||
OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_SPI
|
||||
QUANTUM_LIB_SRC += spi_master.c
|
||||
SPI_DRIVER_REQUIRED = yes
|
||||
SRC += eeprom_driver.c eeprom_spi.c
|
||||
else ifeq ($(strip $(EEPROM_DRIVER)), legacy_stm32_flash)
|
||||
# STM32 Emulated EEPROM, backed by MCU flash (soon to be deprecated)
|
||||
@@ -308,10 +303,10 @@ ifneq ($(strip $(FLASH_DRIVER)), none)
|
||||
else
|
||||
OPT_DEFS += -DFLASH_ENABLE
|
||||
ifeq ($(strip $(FLASH_DRIVER)),spi)
|
||||
SPI_DRIVER_REQUIRED = yes
|
||||
OPT_DEFS += -DFLASH_DRIVER -DFLASH_SPI
|
||||
COMMON_VPATH += $(DRIVER_PATH)/flash
|
||||
SRC += flash_spi.c
|
||||
QUANTUM_LIB_SRC += spi_master.c
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@@ -328,6 +323,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 +338,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 +374,95 @@ endif
|
||||
SRC += $(LIB_PATH)/lib8tion/lib8tion.c
|
||||
CIE1931_CURVE := yes
|
||||
|
||||
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3218)
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31fl3218-simple.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3731)
|
||||
OPT_DEFS += -DIS31FL3731 -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
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)
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31fl3733-simple.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3736)
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31fl3736-simple.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3737)
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31fl3737-simple.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3741)
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31fl3741-simple.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3742a)
|
||||
OPT_DEFS += -DIS31FLCOMMON
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
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
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
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
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
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
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
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)
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led
|
||||
SRC += ckled2001-simple.c
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
SRC += snled27351-simple.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 +478,87 @@ 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)
|
||||
SPI_DRIVER_REQUIRED = yes
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led
|
||||
SRC += aw20216.c
|
||||
QUANTUM_LIB_SRC += spi_master.c
|
||||
SRC += aw20216s.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3218)
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led/issi
|
||||
SRC += is31fl3218.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3731)
|
||||
OPT_DEFS += -DIS31FL3731 -DSTM32_I2C -DHAL_USE_I2C=TRUE
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
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
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
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
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
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
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
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
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
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
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
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
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
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
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
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
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
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)
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led
|
||||
SRC += ckled2001.c
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
SRC += snled27351.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 +605,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
|
||||
@@ -579,35 +618,6 @@ ifeq ($(strip $(BACKLIGHT_ENABLE)), yes)
|
||||
endif
|
||||
endif
|
||||
|
||||
VALID_WS2812_DRIVER_TYPES := bitbang custom i2c pwm spi vendor
|
||||
|
||||
WS2812_DRIVER ?= bitbang
|
||||
ifeq ($(strip $(WS2812_DRIVER_REQUIRED)), yes)
|
||||
ifeq ($(filter $(WS2812_DRIVER),$(VALID_WS2812_DRIVER_TYPES)),)
|
||||
$(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:]'))
|
||||
|
||||
SRC += ws2812_$(strip $(WS2812_DRIVER)).c
|
||||
|
||||
ifeq ($(strip $(PLATFORM)), CHIBIOS)
|
||||
ifeq ($(strip $(WS2812_DRIVER)), pwm)
|
||||
OPT_DEFS += -DSTM32_DMA_REQUIRED=TRUE
|
||||
endif
|
||||
endif
|
||||
|
||||
# add extra deps
|
||||
ifeq ($(strip $(WS2812_DRIVER)), i2c)
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(APA102_DRIVER_REQUIRED)), yes)
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led
|
||||
SRC += apa102.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(CIE1931_CURVE)), yes)
|
||||
OPT_DEFS += -DUSE_CIE1931_CURVE
|
||||
LED_TABLES := yes
|
||||
@@ -724,17 +734,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)
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
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 +766,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
|
||||
@@ -764,10 +774,10 @@ ifeq ($(strip $(OLED_ENABLE)), yes)
|
||||
|
||||
OPT_DEFS += -DOLED_TRANSPORT_$(strip $(shell echo $(OLED_TRANSPORT) | tr '[:lower:]' '[:upper:]'))
|
||||
ifeq ($(strip $(OLED_TRANSPORT)), i2c)
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
endif
|
||||
ifeq ($(strip $(OLED_TRANSPORT)), spi)
|
||||
QUANTUM_LIB_SRC += spi_master.c
|
||||
SPI_DRIVER_REQUIRED = yes
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@@ -775,9 +785,9 @@ endif
|
||||
|
||||
ifeq ($(strip $(ST7565_ENABLE)), yes)
|
||||
OPT_DEFS += -DST7565_ENABLE
|
||||
SPI_DRIVER_REQUIRED = yes
|
||||
COMMON_VPATH += $(DRIVER_PATH)/oled # For glcdfont.h
|
||||
COMMON_VPATH += $(DRIVER_PATH)/lcd
|
||||
QUANTUM_LIB_SRC += spi_master.c
|
||||
SRC += st7565.c
|
||||
endif
|
||||
|
||||
@@ -909,23 +919,22 @@ 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
|
||||
SPI_DRIVER_REQUIRED = yes
|
||||
SRC += $(DRIVER_PATH)/bluetooth/bluetooth.c
|
||||
SRC += $(DRIVER_PATH)/bluetooth/bluefruit_le.cpp
|
||||
QUANTUM_LIB_SRC += analog.c
|
||||
QUANTUM_LIB_SRC += spi_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(BLUETOOTH_DRIVER)), rn42)
|
||||
OPT_DEFS += -DBLUETOOTH_RN42 -DHAL_USE_SERIAL=TRUE
|
||||
UART_DRIVER_REQUIRED = yes
|
||||
SRC += $(DRIVER_PATH)/bluetooth/bluetooth.c
|
||||
SRC += $(DRIVER_PATH)/bluetooth/rn42.c
|
||||
QUANTUM_LIB_SRC += uart.c
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -944,3 +953,47 @@ ifeq ($(strip $(OS_DETECTION_ENABLE)), yes)
|
||||
OPT_DEFS += -DOS_DETECTION_DEBUG_ENABLE
|
||||
endif
|
||||
endif
|
||||
|
||||
VALID_WS2812_DRIVER_TYPES := bitbang custom i2c pwm spi vendor
|
||||
|
||||
WS2812_DRIVER ?= bitbang
|
||||
ifeq ($(strip $(WS2812_DRIVER_REQUIRED)), yes)
|
||||
ifeq ($(filter $(WS2812_DRIVER),$(VALID_WS2812_DRIVER_TYPES)),)
|
||||
$(call CATASTROPHIC_ERROR,Invalid WS2812_DRIVER,WS2812_DRIVER="$(WS2812_DRIVER)" is not a valid WS2812 driver)
|
||||
endif
|
||||
|
||||
OPT_DEFS += -DWS2812_$(strip $(shell echo $(WS2812_DRIVER) | tr '[:lower:]' '[:upper:]'))
|
||||
|
||||
SRC += ws2812_$(strip $(WS2812_DRIVER)).c
|
||||
|
||||
ifeq ($(strip $(PLATFORM)), CHIBIOS)
|
||||
ifeq ($(strip $(WS2812_DRIVER)), pwm)
|
||||
OPT_DEFS += -DSTM32_DMA_REQUIRED=TRUE
|
||||
endif
|
||||
endif
|
||||
|
||||
# add extra deps
|
||||
ifeq ($(strip $(WS2812_DRIVER)), i2c)
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(APA102_DRIVER_REQUIRED)), yes)
|
||||
COMMON_VPATH += $(DRIVER_PATH)/led
|
||||
SRC += apa102.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(I2C_DRIVER_REQUIRED)), yes)
|
||||
OPT_DEFS += -DHAL_USE_I2C=TRUE
|
||||
QUANTUM_LIB_SRC += i2c_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(SPI_DRIVER_REQUIRED)), yes)
|
||||
OPT_DEFS += -DHAL_USE_SPI=TRUE
|
||||
QUANTUM_LIB_SRC += spi_master.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(UART_DRIVER_REQUIRED)), yes)
|
||||
OPT_DEFS += -DHAL_USE_SERIAL=TRUE
|
||||
QUANTUM_LIB_SRC += uart.c
|
||||
endif
|
||||
|
||||
@@ -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,33 +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 \
|
||||
$(REMOVE) $(TARGET).$(FIRMWARE_FORMAT); \
|
||||
$(REMOVE) $(BUILD_DIR)/$(TARGET).{hex,bin,uf2}; \
|
||||
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)
|
||||
|
||||
@@ -37,7 +37,6 @@ GENERIC_FEATURES = \
|
||||
SPACE_CADET \
|
||||
SWAP_HANDS \
|
||||
TAP_DANCE \
|
||||
VELOCIKEY \
|
||||
WPM \
|
||||
DYNAMIC_TAPPING_TERM \
|
||||
TRI_LAYER
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
{
|
||||
"aliases": {
|
||||
/*
|
||||
* ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
|
||||
* │ | │ № │ - │ / │ " │ : │ , │ . │ _ │ ? │ % │ ! │ ; │ │
|
||||
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
|
||||
* │ │ Й │ Ц │ У │ К │ Е │ Н │ Г │ Ш │ Щ │ З │ Х │ Ъ │ ) │
|
||||
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
|
||||
* │ │ Ф │ Ы │ В │ А │ П │ Р │ О │ Л │ Д │ Ж │ Э │ │
|
||||
* ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
|
||||
* │ │ Я │ Ч │ С │ М │ И │ Т │ Ь │ Б │ Ю │ Ё │ │
|
||||
* ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
|
||||
* │ │ │ │ │ │ │ │ │
|
||||
* └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
|
||||
*/
|
||||
"KC_GRV": {
|
||||
"key": "RU_PIPE",
|
||||
"label": "|",
|
||||
}
|
||||
"KC_1": {
|
||||
"key": "RU_NUM",
|
||||
"label": "№",
|
||||
}
|
||||
"KC_2": {
|
||||
"key": "RU_MINS",
|
||||
"label": "-",
|
||||
}
|
||||
"KC_3": {
|
||||
"key": "RU_SLSH",
|
||||
"label": "/",
|
||||
}
|
||||
"KC_4": {
|
||||
"key": "RU_DQUO",
|
||||
"label": "\"",
|
||||
}
|
||||
"KC_5": {
|
||||
"key": "RU_COLN",
|
||||
"label": ":",
|
||||
}
|
||||
"KC_6": {
|
||||
"key": "RU_COMM",
|
||||
"label": ",",
|
||||
}
|
||||
"KC_7": {
|
||||
"key": "RU_DOT",
|
||||
"label": ".",
|
||||
}
|
||||
"KC_8": {
|
||||
"key": "RU_UNDS",
|
||||
"label": "_",
|
||||
}
|
||||
"KC_9": {
|
||||
"key": "RU_QUES",
|
||||
"label": "?",
|
||||
}
|
||||
"KC_0": {
|
||||
"key": "RU_PERC",
|
||||
"label": "%",
|
||||
}
|
||||
"KC_MINS": {
|
||||
"key": "RU_EXLM",
|
||||
"label": "!",
|
||||
}
|
||||
"KC_EQL": {
|
||||
"key": "RU_SCLN",
|
||||
"label": ";",
|
||||
}
|
||||
"KC_Q": {
|
||||
"key": "RU_SHTI",
|
||||
"label": "Й",
|
||||
}
|
||||
"KC_W": {
|
||||
"key": "RU_TSE",
|
||||
"label": "Ц",
|
||||
}
|
||||
"KC_E": {
|
||||
"key": "RU_U",
|
||||
"label": "У",
|
||||
}
|
||||
"KC_R": {
|
||||
"key": "RU_KA",
|
||||
"label": "К",
|
||||
}
|
||||
"KC_T": {
|
||||
"key": "RU_IE",
|
||||
"label": "Е",
|
||||
}
|
||||
"KC_Y": {
|
||||
"key": "RU_EN",
|
||||
"label": "Н",
|
||||
}
|
||||
"KC_U": {
|
||||
"key": "RU_GHE",
|
||||
"label": "Г",
|
||||
}
|
||||
"KC_I": {
|
||||
"key": "RU_SHA",
|
||||
"label": "Ш",
|
||||
}
|
||||
"KC_O": {
|
||||
"key": "RU_SHCH",
|
||||
"label": "Щ",
|
||||
}
|
||||
"KC_P": {
|
||||
"key": "RU_ZE",
|
||||
"label": "З",
|
||||
}
|
||||
"KC_LBRC": {
|
||||
"key": "RU_HA",
|
||||
"label": "Х",
|
||||
}
|
||||
"KC_RBRC": {
|
||||
"key": "RU_HARD",
|
||||
"label": "Ъ",
|
||||
}
|
||||
"KC_BSLS": {
|
||||
"key": "RU_RPRN",
|
||||
"label": ")",
|
||||
}
|
||||
"KC_A": {
|
||||
"key": "RU_EF",
|
||||
"label": "Ф",
|
||||
}
|
||||
"KC_S": {
|
||||
"key": "RU_YERU",
|
||||
"label": "Ы",
|
||||
}
|
||||
"KC_D": {
|
||||
"key": "RU_VE",
|
||||
"label": "В",
|
||||
}
|
||||
"KC_F": {
|
||||
"key": "RU_A",
|
||||
"label": "А",
|
||||
}
|
||||
"KC_G": {
|
||||
"key": "RU_PE",
|
||||
"label": "П",
|
||||
}
|
||||
"KC_H": {
|
||||
"key": "RU_ER",
|
||||
"label": "Р",
|
||||
}
|
||||
"KC_J": {
|
||||
"key": "RU_O",
|
||||
"label": "О",
|
||||
}
|
||||
"KC_K": {
|
||||
"key": "RU_EL",
|
||||
"label": "Л",
|
||||
}
|
||||
"KC_L": {
|
||||
"key": "RU_DE",
|
||||
"label": "Д",
|
||||
}
|
||||
"KC_SCLN": {
|
||||
"key": "RU_ZHE",
|
||||
"label": "Ж",
|
||||
}
|
||||
"KC_QUOT": {
|
||||
"key": "RU_E",
|
||||
"label": "Э",
|
||||
}
|
||||
"KC_Z": {
|
||||
"key": "RU_YA",
|
||||
"label": "Я",
|
||||
}
|
||||
"KC_X": {
|
||||
"key": "RU_CHE",
|
||||
"label": "Ч",
|
||||
}
|
||||
"KC_C": {
|
||||
"key": "RU_ES",
|
||||
"label": "С",
|
||||
}
|
||||
"KC_V": {
|
||||
"key": "RU_EM",
|
||||
"label": "М",
|
||||
}
|
||||
"KC_B": {
|
||||
"key": "RU_I",
|
||||
"label": "И",
|
||||
}
|
||||
"KC_N": {
|
||||
"key": "RU_TE",
|
||||
"label": "Т",
|
||||
}
|
||||
"KC_M": {
|
||||
"key": "RU_SOFT",
|
||||
"label": "Ь",
|
||||
}
|
||||
"KC_COMM": {
|
||||
"key": "RU_BE",
|
||||
"label": "Б",
|
||||
}
|
||||
"KC_DOT": {
|
||||
"key": "RU_YU",
|
||||
"label": "Ю",
|
||||
}
|
||||
"KC_SLSH": {
|
||||
"key": "RU_YO",
|
||||
"label": "Ё",
|
||||
}
|
||||
/* Shifted symbols
|
||||
* ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
|
||||
* │ + │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ = │ \ │ │
|
||||
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
|
||||
* │ │ │ │ │ │ │ │ │ │ │ │ │ │ ( │
|
||||
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
|
||||
* │ │ │ │ │ │ │ │ │ │ │ │ │ │
|
||||
* ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
|
||||
* │ │ │ │ │ │ │ │ │ │ │ │ │
|
||||
* ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
|
||||
* │ │ │ │ │ │ │ │ │
|
||||
* └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
|
||||
*/
|
||||
"S(RU_PIPE)": {
|
||||
"key": "RU_PLUS",
|
||||
"label": "+",
|
||||
}
|
||||
"S(RU_NUM)": {
|
||||
"key": "RU_1",
|
||||
"label": "1",
|
||||
}
|
||||
"S(RU_MINS)": {
|
||||
"key": "RU_2",
|
||||
"label": "2",
|
||||
}
|
||||
"S(RU_SLSH)": {
|
||||
"key": "RU_3",
|
||||
"label": "3",
|
||||
}
|
||||
"S(RU_DQUO)": {
|
||||
"key": "RU_4",
|
||||
"label": "4",
|
||||
}
|
||||
"S(RU_COLN)": {
|
||||
"key": "RU_5",
|
||||
"label": "5",
|
||||
}
|
||||
"S(RU_COMM)": {
|
||||
"key": "RU_6",
|
||||
"label": "6",
|
||||
}
|
||||
"S(RU_DOT)": {
|
||||
"key": "RU_7",
|
||||
"label": "7",
|
||||
}
|
||||
"S(RU_UNDS)": {
|
||||
"key": "RU_8",
|
||||
"label": "8",
|
||||
}
|
||||
"S(RU_QUES)": {
|
||||
"key": "RU_9",
|
||||
"label": "9",
|
||||
}
|
||||
"S(RU_PERC)": {
|
||||
"key": "RU_0",
|
||||
"label": "0",
|
||||
}
|
||||
"S(RU_EXLM)": {
|
||||
"key": "RU_EQL",
|
||||
"label": "=",
|
||||
}
|
||||
"S(RU_SCLN)": {
|
||||
"key": "RU_BSLS",
|
||||
"label": "\\",
|
||||
}
|
||||
"S(RU_RPRN)": {
|
||||
"key": "RU_LPRN",
|
||||
"label": "(",
|
||||
}
|
||||
/* AltGr symbols
|
||||
* ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
|
||||
* │ │ │ │ │ │ │ │ │ ₽ │ │ │ │ │ │
|
||||
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
|
||||
* │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
|
||||
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
|
||||
* │ │ │ │ │ │ │ │ │ │ │ │ │ │
|
||||
* ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
|
||||
* │ │ │ │ │ │ │ │ │ │ │ │ │
|
||||
* ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
|
||||
* │ │ │ │ │ │ │ │ │
|
||||
* └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
|
||||
*/
|
||||
"ALGR(RU_UNDS)": {
|
||||
"key": "RU_RUBL",
|
||||
"label": "₽",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,13 @@
|
||||
"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
|
||||
"EXTERNAL_EEPROM_WP_PIN": {"info_key": "eeprom.i2c.write_protect_pin"},
|
||||
"EXTERNAL_EEPROM_I2C_BASE_ADDRESS": {"info_key": "eeprom.i2c.address", "to_json": false},
|
||||
"EXTERNAL_EEPROM_SPI_SLAVE_SELECT_PIN": {"info_key": "eeprom.spi.chip_select_pin"},
|
||||
"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,7 +70,11 @@
|
||||
"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"},
|
||||
@@ -100,8 +111,12 @@
|
||||
"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"},
|
||||
@@ -134,13 +149,21 @@
|
||||
// Split Keyboard
|
||||
"SOFT_SERIAL_PIN": {"info_key": "split.soft_serial_pin"},
|
||||
"SOFT_SERIAL_SPEED": {"info_key": "split.soft_serial_speed"},
|
||||
"SPLIT_MODS_ENABLE": {"info_key": "split.transport.sync_modifiers", "value_type": "bool"},
|
||||
"SPLIT_TRANSPORT_MIRROR": {"info_key": "split.transport.sync_matrix_state", "value_type": "bool"},
|
||||
"SPLIT_USB_DETECT": {"info_key": "split.usb_detect.enabled", "value_type": "bool"},
|
||||
"SPLIT_USB_TIMEOUT": {"info_key": "split.usb_detect.timeout", "value_type": "int"},
|
||||
"SPLIT_USB_TIMEOUT_POLL": {"info_key": "split.usb_detect.polling_interval", "value_type": "int"},
|
||||
"SPLIT_WATCHDOG_ENABLE": {"info_key": "split.transport.watchdog", "value_type": "bool"},
|
||||
"SPLIT_WATCHDOG_TIMEOUT": {"info_key": "split.transport.watchdog_timeout", "value_type": "int"},
|
||||
"SPLIT_ACTIVITY_ENABLE": {"info_key": "split.transport.sync.activity", "value_type": "bool"},
|
||||
"SPLIT_DETECTED_OS_ENABLE": {"info_key": "split.transport.sync.detected_os", "value_type": "bool"},
|
||||
"SPLIT_HAPTIC_ENABLE": {"info_key": "split.transport.sync.haptic", "value_type": "bool"},
|
||||
"SPLIT_LAYER_STATE_ENABLE": {"info_key": "split.transport.sync.layer_state", "value_type": "bool"},
|
||||
"SPLIT_LED_STATE_ENABLE": {"info_key": "split.transport.sync.indicators", "value_type": "bool"},
|
||||
"SPLIT_TRANSPORT_MIRROR": {"info_key": "split.transport.sync.matrix_state", "value_type": "bool"},
|
||||
"SPLIT_MODS_ENABLE": {"info_key": "split.transport.sync.modifiers", "value_type": "bool"},
|
||||
"SPLIT_OLED_ENABLE": {"info_key": "split.transport.sync.oled", "value_type": "bool"},
|
||||
"SPLIT_ST7565_ENABLE": {"info_key": "split.transport.sync.st7565", "value_type": "bool"},
|
||||
"SPLIT_WPM_ENABLE": {"info_key": "split.transport.sync.wpm", "value_type": "bool"},
|
||||
|
||||
// Tapping
|
||||
"HOLD_ON_OTHER_KEY_PRESS": {"info_key": "tapping.hold_on_other_key_press", "value_type": "bool"},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/ansi"
|
||||
}
|
||||
"keychron/q4": {
|
||||
"target": "keychron/q4/ansi/v1"
|
||||
}
|
||||
"kprepublic/bm40hsrgb": {
|
||||
"target": "kprepublic/bm40hsrgb/rev1"
|
||||
},
|
||||
"kprepublic/bm65hsrgb_iso": {
|
||||
"target": "kprepublic/bm65hsrgb_iso/rev1"
|
||||
},
|
||||
@@ -1208,6 +1236,9 @@
|
||||
"stella": {
|
||||
"target": "hnahkb/stella"
|
||||
},
|
||||
"studiokestra/line_tkl": {
|
||||
"target": "studiokestra/line_friends_tkl"
|
||||
},
|
||||
"suihankey/alpha": {
|
||||
"target": "kakunpc/suihankey/alpha"
|
||||
},
|
||||
@@ -1236,7 +1267,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 +1321,7 @@
|
||||
"target": "ydkb/yd68"
|
||||
},
|
||||
"ymd75": {
|
||||
"target": "ymdk/ymd75"
|
||||
"target": "ymdk/ymd75/rev1"
|
||||
},
|
||||
"ymd96": {
|
||||
"target": "ymdk/ymd96"
|
||||
@@ -1312,5 +1343,9 @@
|
||||
},
|
||||
"zinc/reva": {
|
||||
"target": "25keys/zinc/reva"
|
||||
},
|
||||
// Moved during 2023 Q4 cycle
|
||||
"ymdk/melody96": {
|
||||
"target": "ymdk/melody96/soldered"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +247,36 @@
|
||||
},
|
||||
"eeprom": {
|
||||
"properties": {
|
||||
"driver": {"type": "string"}
|
||||
"driver": {"type": "string"},
|
||||
"i2c": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"device": {"type": "string"},
|
||||
"address": {"$ref": "qmk.definitions.v1#/hex_number_2d"},
|
||||
"write_protect_pin": {"$ref": "qmk.definitions.v1#/mcu_pin"}
|
||||
}
|
||||
},
|
||||
"spi": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"device": {"type": "string"},
|
||||
"chip_select_pin": {"$ref": "qmk.definitions.v1#/mcu_pin"}
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
@@ -390,6 +419,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,
|
||||
@@ -442,6 +475,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,
|
||||
@@ -643,10 +680,32 @@
|
||||
"type": "string",
|
||||
"enum": ["custom", "i2c", "serial", "serial_usart"]
|
||||
},
|
||||
"sync_matrix_state": {"type": "boolean"},
|
||||
"sync_modifiers": {"type": "boolean"},
|
||||
"sync": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"activity": {"type": "boolean"},
|
||||
"detected_os": {"type": "boolean"},
|
||||
"haptic": {"type": "boolean"},
|
||||
"layer_state": {"type": "boolean"},
|
||||
"indicators": {"type": "boolean"},
|
||||
"matrix_state": {"type": "boolean"},
|
||||
"modifiers": {"type": "boolean"},
|
||||
"oled": {"type": "boolean"},
|
||||
"st7565": {"type": "boolean"},
|
||||
"wpm": {"type": "boolean"}
|
||||
}
|
||||
}
|
||||
"watchdog": {"type": "boolean"},
|
||||
"watchdog_timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"}
|
||||
"watchdog_timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"},
|
||||
"sync_matrix_state": {
|
||||
"type": "boolean",
|
||||
"$comment": "Deprecated: use sync.matrix_state instead"
|
||||
},
|
||||
"sync_modifiers": {
|
||||
"type": "boolean",
|
||||
"$comment": "Deprecated: use sync.modifiers instead"
|
||||
}
|
||||
}
|
||||
},
|
||||
"usb_detect": {
|
||||
|
||||
@@ -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)
|
||||
@@ -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
49
docs/apa102_driver.md
Normal 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.
|
||||
@@ -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
|
||||
|
||||
@@ -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`.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -66,6 +66,14 @@ Currently QMK supports 25xx-series chips over SPI. As such, requires a working s
|
||||
`#define EXTERNAL_EEPROM_PAGE_SIZE` | `32` | Page size of the EEPROM in bytes, as specified in the datasheet
|
||||
`#define EXTERNAL_EEPROM_ADDRESS_SIZE` | `2` | The number of bytes to transmit for the memory location within the EEPROM
|
||||
|
||||
Default values and extended descriptions can be found in `drivers/eeprom/eeprom_spi.h`.
|
||||
|
||||
Alternatively, there are pre-defined hardware configurations for available chips/modules:
|
||||
|
||||
Module | Equivalent `#define` | Source
|
||||
-----------------|---------------------------------|------------------------------------------
|
||||
MB85RS64V FRAM | `define EEPROM_SPI_MB85RS64V` | <https://www.adafruit.com/product/1897>
|
||||
|
||||
!> There's no way to determine if there is an SPI EEPROM actually responding. Generally, this will result in reads of nothing but zero.
|
||||
|
||||
## Transient Driver configuration :id=transient-eeprom-driver-configuration
|
||||
@@ -105,11 +113,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 +147,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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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`.
|
||||
|
||||
|
||||
@@ -19,14 +19,14 @@ 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 | |
|
||||
| `LED_DRIVER_ADDR_3` | (Optional) Address for the third LED driver | |
|
||||
| `LED_DRIVER_ADDR_4` | (Optional) Address for the fourth LED driver | |
|
||||
| `IS31FL3731_I2C_ADDRESS_1` | (Required) Address for the first LED driver | |
|
||||
| `IS31FL3731_I2C_ADDRESS_2` | (Optional) Address for the second LED driver | |
|
||||
| `IS31FL3731_I2C_ADDRESS_3` | (Optional) Address for the third LED driver | |
|
||||
| `IS31FL3731_I2C_ADDRESS_4` | (Optional) Address for the fourth LED driver | |
|
||||
|
||||
Here is an example using 2 drivers.
|
||||
|
||||
@@ -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 IS31FL3731_I2C_ADDRESS_1 IS31FL3731_I2C_ADDRESS_GND
|
||||
#define IS31FL3731_I2C_ADDRESS_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 `IS31FL3731_I2C_ADDRESS_1` for one and `IS31FL3731_I2C_ADDRESS_2` for the other one. Then, in `g_is31fl3731_leds`, fill out the correct driver index (0 or 1). If using one address, use `IS31FL3731_I2C_ADDRESS_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
|
||||
|
||||
@@ -167,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:
|
||||
@@ -276,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,
|
||||
@@ -284,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);
|
||||
|
||||
@@ -302,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
|
||||
@@ -334,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
|
||||
@@ -352,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
|
||||
@@ -365,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
|
||||
@@ -399,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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -19,15 +19,15 @@ 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 | |
|
||||
| `DRIVER_ADDR_3` | (Optional) Address for the third RGB driver | |
|
||||
| `DRIVER_ADDR_4` | (Optional) Address for the fourth RGB driver | |
|
||||
| `IS31FL3731_I2C_ADDRESS_1` | (Required) Address for the first RGB driver | |
|
||||
| `IS31FL3731_I2C_ADDRESS_2` | (Optional) Address for the second RGB driver | |
|
||||
| `IS31FL3731_I2C_ADDRESS_3` | (Optional) Address for the third RGB driver | |
|
||||
| `IS31FL3731_I2C_ADDRESS_4` | (Optional) Address for the fourth RGB driver | |
|
||||
|
||||
Here is an example using 2 drivers.
|
||||
|
||||
@@ -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 IS31FL3731_I2C_ADDRESS_1 IS31FL3731_I2C_ADDRESS_GND
|
||||
#define IS31FL3731_I2C_ADDRESS_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 `IS31FL3731_I2C_ADDRESS_1` for one and `IS31FL3731_I2C_ADDRESS_2` for the other one. Then, in `g_is31fl3731_leds`, fill out the correct driver index (0 or 1). If using one address, use `IS31FL3731_I2C_ADDRESS_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,34 +85,34 @@ 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 | |
|
||||
| `DRIVER_ADDR_3` | (Optional) Address for the third RGB driver | |
|
||||
| `DRIVER_ADDR_4` | (Optional) Address for the fourth RGB driver | |
|
||||
| `DRIVER_SYNC_1` | (Optional) Sync configuration for the first RGB driver | 0 |
|
||||
| `DRIVER_SYNC_2` | (Optional) Sync configuration for the second RGB driver | 0 |
|
||||
| `DRIVER_SYNC_3` | (Optional) Sync configuration for the third RGB driver | 0 |
|
||||
| `DRIVER_SYNC_4` | (Optional) Sync configuration for the fourth RGB driver | 0 |
|
||||
| `IS31FL3733_I2C_ADDRESS_1` | (Required) Address for the first RGB driver | |
|
||||
| `IS31FL3733_I2C_ADDRESS_2` | (Optional) Address for the second RGB driver | |
|
||||
| `IS31FL3733_I2C_ADDRESS_3` | (Optional) Address for the third RGB driver | |
|
||||
| `IS31FL3733_I2C_ADDRESS_4` | (Optional) Address for the fourth RGB driver | |
|
||||
| `IS31FL3733_SYNC_1` | (Optional) Sync configuration for the first RGB driver | 0 |
|
||||
| `IS31FL3733_SYNC_2` | (Optional) Sync configuration for the second RGB driver | 0 |
|
||||
| `IS31FL3733_SYNC_3` | (Optional) Sync configuration for the third RGB driver | 0 |
|
||||
| `IS31FL3733_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 IS31FL3733_I2C_ADDRESS_1 IS31FL3733_I2C_ADDRESS_GND_GND
|
||||
#define IS31FL3733_I2C_ADDRESS_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 | |
|
||||
| `IS31FL3736_I2C_ADDRESS_1` | (Required) Address for the first RGB driver | |
|
||||
| `IS31FL3736_I2C_ADDRESS_2` | (Optional) Address for the second RGB driver | |
|
||||
| `IS31FL3736_I2C_ADDRESS_3` | (Optional) Address for the third RGB driver | |
|
||||
| `IS31FL3736_I2C_ADDRESS_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 IS31FL3736_I2C_ADDRESS_1 IS31FL3736_I2C_ADDRESS_GND_GND
|
||||
#define IS31FL3736_I2C_ADDRESS_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 | |
|
||||
| `IS31FL3737_I2C_ADDRESS_1` | (Required) Address for the first RGB driver | |
|
||||
| `IS31FL3737_I2C_ADDRESS_2` | (Optional) Address for the second RGB driver | |
|
||||
| `IS31FL3737_I2C_ADDRESS_3` | (Optional) Address for the third RGB driver | |
|
||||
| `IS31FL3737_I2C_ADDRESS_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 IS31FL3737_I2C_ADDRESS_1 IS31FL3737_I2C_ADDRESS_GND
|
||||
#define IS31FL3737_I2C_ADDRESS_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_CS_PIN_1` | (Required) MCU pin connected to first RGB driver chip select line | B13 |
|
||||
| `AW20216S_CS_PIN_2` | (Optional) MCU pin connected to second RGB driver chip select line | |
|
||||
| `AW20216S_EN_PIN_1` | (Required) MCU pin connected to first RGB driver hardware enable line | C13 |
|
||||
| `AW20216S_EN_PIN_2` | (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_CS_PIN_1 B13
|
||||
#define AW20216S_CS_PIN_2 B14
|
||||
// Hardware enable lines may be connected to the same pin
|
||||
#define DRIVER_1_EN C13
|
||||
#define DRIVER_2_EN C13
|
||||
#define AW20216S_EN_PIN_1 C13
|
||||
#define AW20216S_EN_PIN_2 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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -40,7 +40,7 @@ Eg, if you wanted to set the "Adjust" layer to be layer 5, you'd add this to you
|
||||
| `set_tri_layer_lower_layer(layer)` | Changes the "lower" layer*. |
|
||||
| `set_tri_layer_upper_layer(layer)` | Changes the "upper" layer*. |
|
||||
| `set_tri_layer_adjust_layer(layer)` | Changes the "adjust" layer*. |
|
||||
| `set_tri_layer_layers(lower, upper, adjust)` | Stes the "lower", "upper" and "adjust" layers*. |
|
||||
| `set_tri_layer_layers(lower, upper, adjust)` | Sets the "lower", "upper" and "adjust" layers*. |
|
||||
| `get_tri_layer_lower_layer()` | Gets the current "lower" layer. |
|
||||
| `get_tri_layer_upper_layer()` | Gets the current "upper" layer. |
|
||||
| `get_tri_layer_adjust_layer()` | Gets the current "adjust" layer. |
|
||||
|
||||
@@ -140,7 +140,7 @@ To set the list of enabled input modes, add the `UNICODE_SELECTED_MODES` define
|
||||
```c
|
||||
#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX
|
||||
// or
|
||||
#define UNICODE_SELECTED_MODES UNICODE_MODE_MAC, UNICODE_MODE_WINCOMPOSE
|
||||
#define UNICODE_SELECTED_MODES UNICODE_MODE_MACOS, UNICODE_MODE_WINCOMPOSE
|
||||
```
|
||||
|
||||
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.
|
||||
@@ -151,7 +151,7 @@ If your keyboard has working EEPROM, it will remember the last used input mode a
|
||||
|
||||
### ** macOS **
|
||||
|
||||
**Mode Name:** `UNICODE_MODE_MAC`
|
||||
**Mode Name:** `UNICODE_MODE_MACOS`
|
||||
|
||||
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`.
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -2,6 +2,18 @@
|
||||
|
||||
The I2C Master drivers used in QMK have a set of common functions to allow portability between MCUs.
|
||||
|
||||
## Usage :id=usage
|
||||
|
||||
In most cases, the I2C Master driver code is automatically included if you are using a feature or driver which requires it, such as [OLED](feature_oled_driver.md).
|
||||
|
||||
However, if you need to use the driver standalone, add the following to your `rules.mk`:
|
||||
|
||||
```make
|
||||
I2C_DRIVER_REQUIRED = yes
|
||||
```
|
||||
|
||||
You can then call the I2C API by including `i2c_master.h` in your code.
|
||||
|
||||
## I2C Addressing :id=note-on-i2c-addresses
|
||||
|
||||
All of the addresses expected by this driver should be pushed to the upper 7 bits of the address byte. Setting
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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` を編集し、そこで値を調整して、好みの速度を実現する必要があります。
|
||||
@@ -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).
|
||||
|
||||
@@ -138,7 +138,7 @@ https://github.com/qmk/qmk_firmware/pulls?q=is%3Apr+is%3Aclosed+label%3Akeyboard
|
||||
- standard layouts preferred in these keymaps, if possible
|
||||
- should use [encoder map feature](https://docs.qmk.fm/#/feature_encoders?id=encoder-map), rather than `encoder_update_user()`
|
||||
- default keymap should not enable VIA -- the VIA integration documentation requires a keymap called `via`
|
||||
- submitters can have a personal (or bells-and-whistles) keymap showcasing capabilities in the same PR but it shouldn't be embedded in the 'default' keymap
|
||||
- submitters can add an example (or bells-and-whistles) keymap showcasing capabilities in the same PR but it shouldn't be embedded in the 'default' keymap
|
||||
- submitters can also have a "manufacturer-matching" keymap that mirrors existing functionality of the commercial product, if porting an existing board
|
||||
- Do not include VIA json files in the PR. These do not belong in the QMK repository as they are not used by QMK firmware -- they belong in the [VIA Keyboard Repo](https://github.com/the-via/keyboards)
|
||||
- Do not include KLE json files in the PR. These have no use within QMK.
|
||||
|
||||
@@ -13,22 +13,24 @@ QUANTUM_PAINTER_DRIVERS += ......
|
||||
|
||||
You will also likely need to select an appropriate driver in `rules.mk`, which is listed below.
|
||||
|
||||
!> Quantum Painter is not currently integrated with system-level operations such as disabling displays after a configurable timeout, or when the keyboard goes into suspend. Users will need to handle this manually at the current time.
|
||||
!> Quantum Painter is not currently integrated with system-level operations such as when the keyboard goes into suspend. Users will need to handle this manually at the current time.
|
||||
|
||||
The QMK CLI can be used to convert from normal images such as PNG files or animated GIFs, as well as fonts from TTF files.
|
||||
|
||||
Supported devices:
|
||||
|
||||
| Display Panel | Panel Type | Size | Comms Transport | Driver |
|
||||
|----------------|--------------------|------------------|-----------------|---------------------------------------------|
|
||||
| GC9A01 | RGB LCD (circular) | 240x240 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += gc9a01_spi` |
|
||||
| ILI9163 | RGB LCD | 128x128 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9163_spi` |
|
||||
| ILI9341 | RGB LCD | 240x320 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9341_spi` |
|
||||
| ILI9488 | RGB LCD | 320x480 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9488_spi` |
|
||||
| SSD1351 | RGB OLED | 128x128 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ssd1351_spi` |
|
||||
| ST7735 | RGB LCD | 132x162, 80x160 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += st7735_spi` |
|
||||
| ST7789 | RGB LCD | 240x320, 240x240 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += st7789_spi` |
|
||||
| RGB565 Surface | Virtual | User-defined | None | `QUANTUM_PAINTER_DRIVERS += rgb565_surface` |
|
||||
| Display Panel | Panel Type | Size | Comms Transport | Driver |
|
||||
|---------------|--------------------|------------------|-----------------|------------------------------------------|
|
||||
| GC9A01 | RGB LCD (circular) | 240x240 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += gc9a01_spi` |
|
||||
| ILI9163 | RGB LCD | 128x128 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9163_spi` |
|
||||
| ILI9341 | RGB LCD | 240x320 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9341_spi` |
|
||||
| ILI9488 | RGB LCD | 320x480 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ili9488_spi` |
|
||||
| SSD1351 | RGB OLED | 128x128 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += ssd1351_spi` |
|
||||
| ST7735 | RGB LCD | 132x162, 80x160 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += st7735_spi` |
|
||||
| ST7789 | RGB LCD | 240x320, 240x240 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += st7789_spi` |
|
||||
| SH1106 (SPI) | Monochrome OLED | 128x64 | SPI + D/C + RST | `QUANTUM_PAINTER_DRIVERS += sh1106_spi` |
|
||||
| SH1106 (I2C) | Monochrome OLED | 128x64 | I2C | `QUANTUM_PAINTER_DRIVERS += sh1106_i2c` |
|
||||
| Surface | Virtual | User-defined | None | `QUANTUM_PAINTER_DRIVERS += surface` |
|
||||
|
||||
## Quantum Painter Configuration :id=quantum-painter-config
|
||||
|
||||
@@ -188,7 +190,8 @@ Writing /home/qmk/qmk_firmware/keyboards/my_keeb/generated/noto11.qff.c...
|
||||
|
||||
<!-- tabs:start -->
|
||||
|
||||
### ** Common: Standard TFT (SPI + D/C + RST) **
|
||||
|
||||
### ** LCD **
|
||||
|
||||
Most TFT display panels use a 5-pin interface -- SPI SCK, SPI MOSI, SPI CS, D/C, and RST pins.
|
||||
|
||||
@@ -302,32 +305,6 @@ The maximum number of displays can be configured by changing the following in yo
|
||||
|
||||
Native color format rgb888 is compatible with ILI9488
|
||||
|
||||
#### ** SSD1351 **
|
||||
|
||||
Enabling support for the SSD1351 in Quantum Painter is done by adding the following to `rules.mk`:
|
||||
|
||||
```make
|
||||
QUANTUM_PAINTER_ENABLE = yes
|
||||
QUANTUM_PAINTER_DRIVERS += ssd1351_spi
|
||||
```
|
||||
|
||||
Creating a SSD1351 device in firmware can then be done with the following API:
|
||||
|
||||
```c
|
||||
painter_device_t qp_ssd1351_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);
|
||||
```
|
||||
|
||||
The device handle returned from the `qp_ssd1351_make_spi_device` function can be used to perform all other drawing operations.
|
||||
|
||||
The maximum number of displays can be configured by changing the following in your `config.h` (default is 1):
|
||||
|
||||
```c
|
||||
// 3 displays:
|
||||
#define SSD1351_NUM_DEVICES 3
|
||||
```
|
||||
|
||||
Native color format rgb565 is compatible with SSD1351
|
||||
|
||||
#### ** ST7735 **
|
||||
|
||||
Enabling support for the ST7735 in Quantum Painter is done by adding the following to `rules.mk`:
|
||||
@@ -386,62 +363,139 @@ Native color format rgb565 is compatible with ST7789
|
||||
|
||||
<!-- tabs:end -->
|
||||
|
||||
### ** Common: Surfaces **
|
||||
### ** OLED **
|
||||
|
||||
Quantum Painter has surface drivers which are able to target a buffer in RAM. In general, surfaces keep track of the "dirty" region -- the area that has been drawn to since the last flush -- so that when transferring to the display they can transfer the minimal amount of data to achieve the end result.
|
||||
OLED displays tend to use 5-pin SPI when at larger resolutions, or when using color -- SPI SCK, SPI MOSI, SPI CS, D/C, and RST pins. Smaller OLEDs may use I2C instead.
|
||||
|
||||
!> These generally require significant amounts of RAM, so at large sizes and/or higher bit depths, they may not be usable on all MCUs.
|
||||
When using these displays, either `spi_master` or `i2c_master` must already be correctly configured for both the platform and panel you're building for.
|
||||
|
||||
For SPI, the pin assignments for SPI CS, D/C, and RST are specified during device construction -- for I2C the panel's address is specified instead.
|
||||
|
||||
<!-- tabs:start -->
|
||||
|
||||
#### ** RGB565 Surface **
|
||||
#### ** SSD1351 **
|
||||
|
||||
Enabling support for RGB565 surfaces in Quantum Painter is done by adding the following to `rules.mk`:
|
||||
Enabling support for the SSD1351 in Quantum Painter is done by adding the following to `rules.mk`:
|
||||
|
||||
```make
|
||||
QUANTUM_PAINTER_ENABLE = yes
|
||||
QUANTUM_PAINTER_DRIVERS += rgb565_surface
|
||||
QUANTUM_PAINTER_DRIVERS += ssd1351_spi
|
||||
```
|
||||
|
||||
Creating a RGB565 surface in firmware can then be done with the following API:
|
||||
Creating a SSD1351 device in firmware can then be done with the following API:
|
||||
|
||||
```c
|
||||
painter_device_t qp_rgb565_make_surface(uint16_t panel_width, uint16_t panel_height, void *buffer);
|
||||
painter_device_t qp_ssd1351_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);
|
||||
```
|
||||
|
||||
The `buffer` is a user-supplied area of memory, and is assumed to be of the size `sizeof(uint16_t) * panel_width * panel_height`.
|
||||
The device handle returned from the `qp_ssd1351_make_spi_device` function can be used to perform all other drawing operations.
|
||||
|
||||
The device handle returned from the `qp_rgb565_make_surface` function can be used to perform all other drawing operations.
|
||||
The maximum number of displays can be configured by changing the following in your `config.h` (default is 1):
|
||||
|
||||
```c
|
||||
// 3 displays:
|
||||
#define SSD1351_NUM_DEVICES 3
|
||||
```
|
||||
|
||||
Native color format rgb565 is compatible with SSD1351
|
||||
|
||||
#### ** SH1106 **
|
||||
|
||||
Enabling support for the SH1106 in Quantum Painter is done by adding the following to `rules.mk`:
|
||||
|
||||
```make
|
||||
QUANTUM_PAINTER_ENABLE = yes
|
||||
# For SPI:
|
||||
QUANTUM_PAINTER_DRIVERS += sh1106_spi
|
||||
# For I2C:
|
||||
QUANTUM_PAINTER_DRIVERS += sh1106_i2c
|
||||
```
|
||||
|
||||
Creating a SH1106 device in firmware can then be done with the following APIs:
|
||||
|
||||
```c
|
||||
// SPI-based SH1106:
|
||||
painter_device_t qp_sh1106_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);
|
||||
// I2C-based SH1106:
|
||||
painter_device_t qp_sh1106_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address);
|
||||
```
|
||||
|
||||
The device handle returned from the `qp_sh1106_make_???_device` function can be used to perform all other drawing operations.
|
||||
|
||||
The maximum number of displays of each type can be configured by changing the following in your `config.h` (default is 1):
|
||||
|
||||
```c
|
||||
// 3 SPI displays:
|
||||
#define SH1106_NUM_SPI_DEVICES 3
|
||||
// 3 I2C displays:
|
||||
#define SH1106_NUM_I2C_DEVICES 3
|
||||
```
|
||||
|
||||
Native color format mono2 is compatible with SH1106
|
||||
|
||||
<!-- tabs:end -->
|
||||
|
||||
### ** Surface **
|
||||
|
||||
Quantum Painter has a surface driver which is able to target a buffer in RAM. In general, surfaces keep track of the "dirty" region -- the area that has been drawn to since the last flush -- so that when transferring to the display they can transfer the minimal amount of data to achieve the end result.
|
||||
|
||||
!> These generally require significant amounts of RAM, so at large sizes and/or higher bit depths, they may not be usable on all MCUs.
|
||||
|
||||
Enabling support for surfaces in Quantum Painter is done by adding the following to `rules.mk`:
|
||||
|
||||
```make
|
||||
QUANTUM_PAINTER_ENABLE = yes
|
||||
QUANTUM_PAINTER_DRIVERS += surface
|
||||
```
|
||||
|
||||
Creating a surface in firmware can then be done with the following APIs:
|
||||
|
||||
```c
|
||||
// 16bpp RGB565 surface:
|
||||
painter_device_t qp_make_rgb565_surface(uint16_t panel_width, uint16_t panel_height, void *buffer);
|
||||
// 1bpp monochrome surface:
|
||||
painter_device_t qp_make_mono1bpp_surface(uint16_t panel_width, uint16_t panel_height, void *buffer);
|
||||
```
|
||||
|
||||
The `buffer` is a user-supplied area of memory, which can be statically allocated using `SURFACE_REQUIRED_BUFFER_BYTE_SIZE`:
|
||||
|
||||
```c
|
||||
// Buffer required for a 240x80 16bpp surface:
|
||||
uint8_t framebuffer[SURFACE_REQUIRED_BUFFER_BYTE_SIZE(240, 80, 16)];
|
||||
```
|
||||
|
||||
The device handle returned from the `qp_make_?????_surface` function can be used to perform all other drawing operations.
|
||||
|
||||
Example:
|
||||
|
||||
```c
|
||||
static painter_device_t my_surface;
|
||||
static uint16_t my_framebuffer[320 * 240]; // Allocate a buffer for a 320x240 RGB565 display
|
||||
static uint8_t my_framebuffer[SURFACE_REQUIRED_BUFFER_BYTE_SIZE(240, 80, 16)]; // Allocate a buffer for a 16bpp 240x80 RGB565 display
|
||||
void keyboard_post_init_kb(void) {
|
||||
my_surface = qp_rgb565_make_surface(320, 240, my_framebuffer);
|
||||
my_surface = qp_rgb565_make_surface(240, 80, my_framebuffer);
|
||||
qp_init(my_surface, QP_ROTATION_0);
|
||||
keyboard_post_init_user();
|
||||
}
|
||||
```
|
||||
|
||||
The maximum number of RGB565 surfaces can be configured by changing the following in your `config.h` (default is 1):
|
||||
The maximum number of surfaces can be configured by changing the following in your `config.h` (default is 1):
|
||||
|
||||
```c
|
||||
// 3 surfaces:
|
||||
#define RGB565_SURFACE_NUM_DEVICES 3
|
||||
#define SURFACE_NUM_DEVICES 3
|
||||
```
|
||||
|
||||
To transfer the contents of the RGB565 surface to another display, the following API can be invoked:
|
||||
To transfer the contents of the surface to another display of the same pixel format, the following API can be invoked:
|
||||
|
||||
```c
|
||||
bool qp_rgb565_surface_draw(painter_device_t surface, painter_device_t display, uint16_t x, uint16_t y);
|
||||
bool qp_surface_draw(painter_device_t surface, painter_device_t display, uint16_t x, uint16_t y);
|
||||
```
|
||||
|
||||
The `surface` is the surface to copy out from. The `display` is the target display to draw into. `x` and `y` are the target location to draw the surface pixel data. Under normal circumstances, the location should be consistent, as the dirty region is calculated with respect to the `x` and `y` coordinates -- changing those will result in partial, overlapping draws.
|
||||
|
||||
?> Calling `qp_flush()` on the surface resets its dirty region. Copying the surface contents to the display also automatically resets the dirty region.
|
||||
!> The surface and display panel must have the same native pixel format.
|
||||
|
||||
<!-- tabs:end -->
|
||||
?> Calling `qp_flush()` on the surface resets its dirty region. Copying the surface contents to the display also automatically resets the dirty region.
|
||||
|
||||
<!-- tabs:end -->
|
||||
|
||||
@@ -857,13 +911,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 **
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -88,7 +88,7 @@ You can create `info.json` files at every level under `qmk_firmware/keyboards/<k
|
||||
|
||||
## APA102 :id=apa102
|
||||
|
||||
Configures the APA102 driver.
|
||||
Configures the [APA102](apa102_driver.md) driver.
|
||||
|
||||
* `apa102`
|
||||
* `clock_pin` (Required)
|
||||
@@ -202,6 +202,13 @@ Configures the [EEPROM](eeprom_driver.md) driver.
|
||||
* `driver`
|
||||
* The EEPROM backend to use. Must be one of `custom`, `i2c`, `legacy_stm32_flash`, `spi`, `transient`, `vendor`, `wear_leveling`.
|
||||
* Default: `"vendor"`
|
||||
* `wear_leveling`
|
||||
* `driver`
|
||||
* The driver to use. Must be one of `embedded_flash`, `legacy`, `rp2040_flash`, `spi_flash`, `custom`.
|
||||
* `backing_size`
|
||||
* Number of bytes used by the wear-leveling algorithm for its underlying storage, and needs to be a multiple of the logical size.
|
||||
* `logical_size`
|
||||
* Number of bytes “exposed” to the rest of QMK and denotes the size of the usable EEPROM.
|
||||
|
||||
## Encoder :id=encoder
|
||||
|
||||
@@ -322,7 +329,7 @@ Configures the [LED Matrix](feature_led_matrix.md) feature.
|
||||
* The centroid (geometric center) of the LEDs. Used for certain effects.
|
||||
* Default: `[112, 32]`
|
||||
* `driver` (Required)
|
||||
* The driver to use. Must be one of `ckled2001`, `custom`, `is31fl3731`, `is31fl3742a`, `is31fl3743a`, `is31fl3745`, `is31fl3746a`.
|
||||
* The driver to use. Must be one of `custom`, `is31fl3218`, `is31fl3731`, `is31fl3733`, `is31fl3736`, `is31fl3737`, `is31fl3741`, `is31fl3742a`, `is31fl3743a`, `is31fl3745`, `is31fl3746a`, `snled27351`.
|
||||
* `layout` (Required)
|
||||
* List of LED configuration dictionaries. Each dictionary contains:
|
||||
* `flags` (Required)
|
||||
@@ -335,9 +342,21 @@ Configures the [LED Matrix](feature_led_matrix.md) feature.
|
||||
* The key matrix position associated with the LED.
|
||||
* Example: `[0, 2]`
|
||||
* Example: `{"matrix": [2, 1], "x": 20, "y": 48, "flags": 2}`
|
||||
* `led_flush_limit`
|
||||
* Limits in milliseconds how frequently an animation will update the LEDs.
|
||||
* Default: `16`
|
||||
* `led_process_limit`
|
||||
* Limits the number of LEDs to process in an animation per task run (increases keyboard responsiveness).
|
||||
* Default: `led_count / 5`
|
||||
* `max_brightness`
|
||||
* The maximum value which brightness is scaled to, from 0 to 255.
|
||||
* Default: `255`
|
||||
* `react_on_keyup`
|
||||
* Animations react to keyup instead of keydown.
|
||||
* Default: `false`
|
||||
* `sleep`
|
||||
* Turn off the LEDs when the host goes to sleep.
|
||||
* Default: `false`
|
||||
* `speed_steps`
|
||||
* The number of speed adjustment steps.
|
||||
* Default: `16`
|
||||
@@ -523,7 +542,7 @@ Configures the [RGB Matrix](feature_rgb_matrix.md) feature.
|
||||
* The centroid (geometric center) of the LEDs. Used for certain effects.
|
||||
* Default: `[112, 32]`
|
||||
* `driver` (Required)
|
||||
* The driver to use. Must be one of `aw20216`, `ckled2001`, `custom`, `is31fl3731`, `is31fl3733`, `is31fl3736`, `is31fl3737`, `is31fl3741`, `is31fl3742a`, `is31fl3743a`, `is31fl3745`, `is31fl3746a`, `ws2812`.
|
||||
* The driver to use. Must be one of `aw20216s`, `custom`, `is31fl3218`, `is31fl3731`, `is31fl3733`, `is31fl3736`, `is31fl3737`, `is31fl3741`, `is31fl3742a`, `is31fl3743a`, `is31fl3745`, `is31fl3746a`, `snled27351`, `ws2812`.
|
||||
* `hue_steps`
|
||||
* The number of hue adjustment steps.
|
||||
* Default: `8`
|
||||
@@ -539,12 +558,24 @@ Configures the [RGB Matrix](feature_rgb_matrix.md) feature.
|
||||
* The key matrix position associated with the LED.
|
||||
* Example: `[0, 2]`
|
||||
* Example: `{"matrix": [2, 1], "x": 20, "y": 48, "flags": 2}`
|
||||
* `led_flush_limit`
|
||||
* Limits in milliseconds how frequently an animation will update the LEDs.
|
||||
* Default: `16`
|
||||
* `led_process_limit`
|
||||
* Limits the number of LEDs to process in an animation per task run (increases keyboard responsiveness).
|
||||
* Default: `led_count / 5`
|
||||
* `max_brightness`
|
||||
* The maximum value which the HSV "V" component is scaled to, from 0 to 255.
|
||||
* Default: `255`
|
||||
* `react_on_keyup`
|
||||
* Animations react to keyup instead of keydown.
|
||||
* Default: `false`
|
||||
* `sat_steps`
|
||||
* The number of saturation adjustment steps.
|
||||
* Default: `16`
|
||||
* `sleep`
|
||||
* Turn off the LEDs when the host goes to sleep.
|
||||
* Default: `false`
|
||||
* `speed_steps`
|
||||
* The number of speed adjustment steps.
|
||||
* Default: `16`
|
||||
@@ -602,12 +633,37 @@ Configures the [Split Keyboard](feature_split_keyboard.md) feature.
|
||||
* `transport`
|
||||
* `protocol`
|
||||
* The split transport protocol to use. Must be one of `custom`, `i2c`, `serial`, `serial_usart`.
|
||||
* `sync_matrix_state`
|
||||
* Mirror the main/primary half's matrix state to the secondary half.
|
||||
* Default: `false`
|
||||
* `sync_modifiers`
|
||||
* Mirror the modifier state to the secondary half.
|
||||
* Default: `false`
|
||||
* `sync`
|
||||
* `activity`
|
||||
* Mirror the activity timestamps to the secondary half.
|
||||
* Default: `false`
|
||||
* `detected_os`
|
||||
* Mirror the [detected OS](feature_os_detection.md) to the secondary half.
|
||||
* Default: `false`
|
||||
* `haptic`
|
||||
* Mirror the haptic state and process haptic feedback to the secondary half.
|
||||
* Default: `false`
|
||||
* `layer_state`
|
||||
* Mirror the layer state to the secondary half.
|
||||
* Default: `false`
|
||||
* `indicators`
|
||||
* Mirror the indicator state to the secondary half.
|
||||
* Default: `false`
|
||||
* `matrix_state`
|
||||
* Mirror the main/primary half's matrix state to the secondary half.
|
||||
* Default: `false`
|
||||
* `modifiers`
|
||||
* Mirror the modifier state to the secondary half.
|
||||
* Default: `false`
|
||||
* `oled`
|
||||
* Mirror the OLED on/off status to the secondary half.
|
||||
* Default: `false`
|
||||
* `st7565`
|
||||
* Mirror the ST7565 on/off status to the secondary half.
|
||||
* Default: `false`
|
||||
* `wpm`
|
||||
* Mirror the current WPM value to the secondary half.
|
||||
* Default: `false`
|
||||
* `watchdog`
|
||||
* Reboot the secondary half if it loses connection.
|
||||
* Default: `false`
|
||||
|
||||
@@ -2,6 +2,18 @@
|
||||
|
||||
The SPI Master drivers used in QMK have a set of common functions to allow portability between MCUs.
|
||||
|
||||
## Usage :id=usage
|
||||
|
||||
In most cases, the SPI Master driver code is automatically included if you are using a feature or driver which requires it, such as [OLED](feature_oled_driver.md).
|
||||
|
||||
However, if you need to use the driver standalone, add the following to your `rules.mk`:
|
||||
|
||||
```make
|
||||
SPI_DRIVER_REQUIRED = yes
|
||||
```
|
||||
|
||||
You can then call the SPI API by including `spi_master.h` in your code.
|
||||
|
||||
## AVR Configuration :id=avr-configuration
|
||||
|
||||
No special setup is required - just connect the `SS`, `SCK`, `MOSI` and `MISO` pins of your SPI devices to the matching pins on the MCU:
|
||||
@@ -49,6 +61,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,6 +4,18 @@ The UART drivers used in QMK have a set of common functions to allow portability
|
||||
|
||||
Currently, this driver does not support enabling hardware flow control (the `RTS` and `CTS` pins) if available, but may do so in future.
|
||||
|
||||
## Usage :id=usage
|
||||
|
||||
In most cases, the UART driver code is automatically included if you are using a feature or driver which requires it.
|
||||
|
||||
However, if you need to use the driver standalone, add the following to your `rules.mk`:
|
||||
|
||||
```make
|
||||
UART_DRIVER_REQUIRED = yes
|
||||
```
|
||||
|
||||
You can then call the UART API by including `uart.h` in your code.
|
||||
|
||||
## AVR Configuration :id=avr-configuration
|
||||
|
||||
No special setup is required - just connect the `RX` and `TX` pins of your UART device to the opposite pins on the MCU:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -16,6 +16,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
Default device configurations:
|
||||
|
||||
For the Adafruit SPI Non-Volatile FRAM Breakout: https://www.adafruit.com/product/1897
|
||||
#define EEPROM_SPI_MB85RS64V
|
||||
*/
|
||||
#if defined(EEPROM_SPI_MB85RS64V)
|
||||
# define EXTERNAL_EEPROM_BYTE_COUNT 8192
|
||||
# define EXTERNAL_EEPROM_PAGE_SIZE 64 // it's FRAM, so it doesn't actually matter, this just sets the RAM buffer
|
||||
# define EXTERNAL_EEPROM_ADDRESS_SIZE 2
|
||||
#endif
|
||||
|
||||
/*
|
||||
The slave select pin of the EEPROM.
|
||||
This needs to be a normal GPIO pin_t value, such as A7.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
175
drivers/led/aw20216s.c
Normal file
175
drivers/led/aw20216s.c
Normal file
@@ -0,0 +1,175 @@
|
||||
/* 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;
|
||||
}
|
||||
|
||||
void aw20216s_flush(void) {
|
||||
aw20216s_update_pwm_buffers(AW20216S_CS_PIN_1, 0);
|
||||
#if defined(AW20216S_CS_PIN_2)
|
||||
aw20216s_update_pwm_buffers(AW20216S_CS_PIN_2, 1);
|
||||
#endif
|
||||
}
|
||||
@@ -21,19 +21,54 @@
|
||||
#include "progmem.h"
|
||||
#include "gpio.h"
|
||||
|
||||
typedef struct aw_led {
|
||||
// ======== 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_CS_PIN_1 DRIVER_1_CS
|
||||
#endif
|
||||
#ifdef DRIVER_2_CS
|
||||
# define AW20216S_CS_PIN_2 DRIVER_2_CS
|
||||
#endif
|
||||
#ifdef DRIVER_1_EN
|
||||
# define AW20216S_EN_PIN_1 DRIVER_1_EN
|
||||
#endif
|
||||
#ifdef DRIVER_2_EN
|
||||
# define AW20216S_EN_PIN_2 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;
|
||||
} aw_led;
|
||||
} aw20216s_led_t;
|
||||
|
||||
extern const aw_led PROGMEM g_aw_leds[RGB_MATRIX_LED_COUNT];
|
||||
extern const aw20216s_led_t PROGMEM g_aw20216s_leds[RGB_MATRIX_LED_COUNT];
|
||||
|
||||
void aw20216_init(pin_t cs_pin, pin_t en_pin);
|
||||
void aw20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void aw20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
|
||||
void aw20216_update_pwm_buffers(pin_t cs_pin, uint8_t index);
|
||||
void 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);
|
||||
|
||||
void aw20216s_flush(void);
|
||||
|
||||
#define CS1_SW1 0x00
|
||||
#define CS2_SW1 0x01
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
143
drivers/led/issi/is31fl3218-simple.c
Normal file
143
drivers/led/issi/is31fl3218-simple.c
Normal 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;
|
||||
}
|
||||
}
|
||||
60
drivers/led/issi/is31fl3218-simple.h
Normal file
60
drivers/led/issi/is31fl3218-simple.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -242,3 +211,16 @@ void is31fl3731_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
g_led_control_registers_update_required[index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void is31fl3731_flush(void) {
|
||||
is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_1, 0);
|
||||
#if defined(IS31FL3731_I2C_ADDRESS_2)
|
||||
is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_2, 1);
|
||||
# if defined(IS31FL3731_I2C_ADDRESS_3)
|
||||
is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_3, 2);
|
||||
# if defined(IS31FL3731_I2C_ADDRESS_4)
|
||||
is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_4, 3);
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -20,15 +20,49 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "progmem.h"
|
||||
|
||||
typedef struct is31_led {
|
||||
// ======== DEPRECATED DEFINES - DO NOT USE ========
|
||||
#ifdef LED_DRIVER_ADDR_1
|
||||
# define IS31FL3731_I2C_ADDRESS_1 LED_DRIVER_ADDR_1
|
||||
#endif
|
||||
#ifdef LED_DRIVER_ADDR_2
|
||||
# define IS31FL3731_I2C_ADDRESS_2 LED_DRIVER_ADDR_2
|
||||
#endif
|
||||
#ifdef LED_DRIVER_ADDR_3
|
||||
# define IS31FL3731_I2C_ADDRESS_3 LED_DRIVER_ADDR_3
|
||||
#endif
|
||||
#ifdef LED_DRIVER_ADDR_4
|
||||
# define IS31FL3731_I2C_ADDRESS_4 LED_DRIVER_ADDR_4
|
||||
#endif
|
||||
#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);
|
||||
@@ -46,6 +80,8 @@ void is31fl3731_set_led_control_register(uint8_t index, bool value);
|
||||
void is31fl3731_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void is31fl3731_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
|
||||
void is31fl3731_flush(void);
|
||||
|
||||
#define C1_1 0x24
|
||||
#define C1_2 0x25
|
||||
#define C1_3 0x26
|
||||
|
||||
@@ -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;
|
||||
@@ -245,3 +223,16 @@ void is31fl3731_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
}
|
||||
g_led_control_registers_update_required[index] = false;
|
||||
}
|
||||
|
||||
void is31fl3731_flush(void) {
|
||||
is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_1, 0);
|
||||
#if defined(IS31FL3731_I2C_ADDRESS_2)
|
||||
is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_2, 1);
|
||||
# if defined(IS31FL3731_I2C_ADDRESS_3)
|
||||
is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_3, 2);
|
||||
# if defined(IS31FL3731_I2C_ADDRESS_4)
|
||||
is31fl3731_update_pwm_buffers(IS31FL3731_I2C_ADDRESS_4, 3);
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -19,17 +19,51 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "progmem.h"
|
||||
|
||||
typedef struct is31_led {
|
||||
// ======== DEPRECATED DEFINES - DO NOT USE ========
|
||||
#ifdef DRIVER_ADDR_1
|
||||
# define IS31FL3731_I2C_ADDRESS_1 DRIVER_ADDR_1
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_2
|
||||
# define IS31FL3731_I2C_ADDRESS_2 DRIVER_ADDR_2
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_3
|
||||
# define IS31FL3731_I2C_ADDRESS_3 DRIVER_ADDR_3
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_4
|
||||
# define IS31FL3731_I2C_ADDRESS_4 DRIVER_ADDR_4
|
||||
#endif
|
||||
#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);
|
||||
@@ -47,6 +81,8 @@ void is31fl3731_set_led_control_register(uint8_t index, bool red, bool green, bo
|
||||
void is31fl3731_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void is31fl3731_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
|
||||
void is31fl3731_flush(void);
|
||||
|
||||
#define C1_1 0x24
|
||||
#define C1_2 0x25
|
||||
#define C1_3 0x26
|
||||
|
||||
@@ -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,11 +225,24 @@ 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]);
|
||||
}
|
||||
g_led_control_registers_update_required[index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void is31fl3733_flush(void) {
|
||||
is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_1, 0);
|
||||
#if defined(IS31FL3733_I2C_ADDRESS_2)
|
||||
is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_2, 1);
|
||||
# if defined(IS31FL3733_I2C_ADDRESS_3)
|
||||
is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_3, 2);
|
||||
# if defined(IS31FL3733_I2C_ADDRESS_4)
|
||||
is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_4, 3);
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -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,21 @@ 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
|
||||
void is31fl3733_flush(void);
|
||||
|
||||
#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
|
||||
|
||||
@@ -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,39 @@ 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;
|
||||
}
|
||||
|
||||
void is31fl3733_flush(void) {
|
||||
is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_1, 0);
|
||||
#if defined(IS31FL3733_I2C_ADDRESS_2)
|
||||
is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_2, 1);
|
||||
# if defined(IS31FL3733_I2C_ADDRESS_3)
|
||||
is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_3, 2);
|
||||
# if defined(IS31FL3733_I2C_ADDRESS_4)
|
||||
is31fl3733_update_pwm_buffers(IS31FL3733_I2C_ADDRESS_4, 3);
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -23,14 +23,90 @@
|
||||
#include <stdbool.h>
|
||||
#include "progmem.h"
|
||||
|
||||
typedef struct is31_led {
|
||||
// ======== DEPRECATED DEFINES - DO NOT USE ========
|
||||
#ifdef DRIVER_ADDR_1
|
||||
# define IS31FL3733_I2C_ADDRESS_1 DRIVER_ADDR_1
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_2
|
||||
# define IS31FL3733_I2C_ADDRESS_2 DRIVER_ADDR_2
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_3
|
||||
# define IS31FL3733_I2C_ADDRESS_3 DRIVER_ADDR_3
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_4
|
||||
# define IS31FL3733_I2C_ADDRESS_4 DRIVER_ADDR_4
|
||||
#endif
|
||||
#ifdef DRIVER_SYNC_1
|
||||
# define IS31FL3733_SYNC_1 DRIVER_SYNC_1
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_2
|
||||
# define IS31FL3733_SYNC_2 DRIVER_SYNC_2
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_3
|
||||
# define IS31FL3733_SYNC_3 DRIVER_SYNC_3
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_4
|
||||
# define IS31FL3733_SYNC_4 DRIVER_SYNC_4
|
||||
#endif
|
||||
#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 +124,21 @@ 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
|
||||
void is31fl3733_flush(void);
|
||||
|
||||
#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
|
||||
|
||||
235
drivers/led/issi/is31fl3736-simple.c
Normal file
235
drivers/led/issi/is31fl3736-simple.c
Normal file
@@ -0,0 +1,235 @@
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
void is31fl3736_flush(void) {
|
||||
is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_1, 0);
|
||||
#if defined(IS31FL3736_I2C_ADDRESS_2)
|
||||
is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_2, 1);
|
||||
# if defined(IS31FL3736_I2C_ADDRESS_3)
|
||||
is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_3, 2);
|
||||
# if defined(IS31FL3736_I2C_ADDRESS_4)
|
||||
is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_4, 3);
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
220
drivers/led/issi/is31fl3736-simple.h
Normal file
220
drivers/led/issi/is31fl3736-simple.h
Normal file
@@ -0,0 +1,220 @@
|
||||
/* 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);
|
||||
|
||||
void is31fl3736_flush(void);
|
||||
|
||||
#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
|
||||
@@ -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,41 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
void is31fl3736_flush(void) {
|
||||
is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_1, 0);
|
||||
#if defined(IS31FL3736_I2C_ADDRESS_2)
|
||||
is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_2, 1);
|
||||
# if defined(IS31FL3736_I2C_ADDRESS_3)
|
||||
is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_3, 2);
|
||||
# if defined(IS31FL3736_I2C_ADDRESS_4)
|
||||
is31fl3736_update_pwm_buffers(IS31FL3736_I2C_ADDRESS_4, 3);
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -19,28 +19,78 @@
|
||||
|
||||
#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_ADDR_1
|
||||
# define IS31FL3736_I2C_ADDRESS_1 DRIVER_ADDR_1
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_2
|
||||
# define IS31FL3736_I2C_ADDRESS_2 DRIVER_ADDR_2
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_3
|
||||
# define IS31FL3736_I2C_ADDRESS_3 DRIVER_ADDR_3
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_4
|
||||
# define IS31FL3736_I2C_ADDRESS_4 DRIVER_ADDR_4
|
||||
#endif
|
||||
#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 +101,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 +108,22 @@ 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
|
||||
void is31fl3736_flush(void);
|
||||
|
||||
#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
|
||||
|
||||
232
drivers/led/issi/is31fl3737-simple.c
Normal file
232
drivers/led/issi/is31fl3737-simple.c
Normal file
@@ -0,0 +1,232 @@
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
void is31fl3737_flush(void) {
|
||||
is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_1, 0);
|
||||
#if defined(IS31FL3737_I2C_ADDRESS_2)
|
||||
is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_2, 1);
|
||||
# if defined(IS31FL3737_I2C_ADDRESS_3)
|
||||
is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_3, 2);
|
||||
# if defined(IS31FL3737_I2C_ADDRESS_4)
|
||||
is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_4, 3);
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
258
drivers/led/issi/is31fl3737-simple.h
Normal file
258
drivers/led/issi/is31fl3737-simple.h
Normal file
@@ -0,0 +1,258 @@
|
||||
/* 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);
|
||||
|
||||
void is31fl3737_flush(void);
|
||||
|
||||
#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
|
||||
@@ -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,35 @@ 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;
|
||||
}
|
||||
|
||||
void is31fl3737_flush(void) {
|
||||
is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_1, 0);
|
||||
#if defined(IS31FL3737_I2C_ADDRESS_2)
|
||||
is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_2, 1);
|
||||
# if defined(IS31FL3737_I2C_ADDRESS_3)
|
||||
is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_3, 2);
|
||||
# if defined(IS31FL3737_I2C_ADDRESS_4)
|
||||
is31fl3737_update_pwm_buffers(IS31FL3737_I2C_ADDRESS_4, 3);
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -21,17 +21,69 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "progmem.h"
|
||||
|
||||
typedef struct is31_led {
|
||||
// ======== DEPRECATED DEFINES - DO NOT USE ========
|
||||
#ifdef DRIVER_ADDR_1
|
||||
# define IS31FL3737_I2C_ADDRESS_1 DRIVER_ADDR_1
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_2
|
||||
# define IS31FL3737_I2C_ADDRESS_2 DRIVER_ADDR_2
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_3
|
||||
# define IS31FL3737_I2C_ADDRESS_3 DRIVER_ADDR_3
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_4
|
||||
# define IS31FL3737_I2C_ADDRESS_4 DRIVER_ADDR_4
|
||||
#endif
|
||||
#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 +101,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);
|
||||
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
|
||||
void is31fl3737_flush(void);
|
||||
|
||||
#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
|
||||
|
||||
266
drivers/led/issi/is31fl3741-simple.c
Normal file
266
drivers/led/issi/is31fl3741-simple.c
Normal file
@@ -0,0 +1,266 @@
|
||||
/* 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;
|
||||
}
|
||||
|
||||
void is31fl3741_flush(void) {
|
||||
is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_1, 0);
|
||||
#if defined(IS31FL3741_I2C_ADDRESS_2)
|
||||
is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_2, 1);
|
||||
# if defined(IS31FL3741_I2C_ADDRESS_3)
|
||||
is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_3, 2);
|
||||
# if defined(IS31FL3741_I2C_ADDRESS_4)
|
||||
is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_4, 3);
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
473
drivers/led/issi/is31fl3741-simple.h
Normal file
473
drivers/led/issi/is31fl3741-simple.h
Normal file
@@ -0,0 +1,473 @@
|
||||
/* 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);
|
||||
|
||||
void is31fl3741_flush(void);
|
||||
|
||||
#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
|
||||
@@ -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,10 +262,23 @@ 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;
|
||||
|
||||
g_scaling_registers_update_required[pled->driver] = true;
|
||||
}
|
||||
|
||||
void is31fl3741_flush(void) {
|
||||
is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_1, 0);
|
||||
#if defined(IS31FL3741_I2C_ADDRESS_2)
|
||||
is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_2, 1);
|
||||
# if defined(IS31FL3741_I2C_ADDRESS_3)
|
||||
is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_3, 2);
|
||||
# if defined(IS31FL3741_I2C_ADDRESS_4)
|
||||
is31fl3741_update_pwm_buffers(IS31FL3741_I2C_ADDRESS_4, 3);
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -23,14 +23,67 @@
|
||||
#include <stdbool.h>
|
||||
#include "progmem.h"
|
||||
|
||||
typedef struct is31_led {
|
||||
// ======== DEPRECATED DEFINES - DO NOT USE ========
|
||||
#ifdef DRIVER_ADDR_1
|
||||
# define IS31FL3741_I2C_ADDRESS_1 DRIVER_ADDR_1
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_2
|
||||
# define IS31FL3741_I2C_ADDRESS_2 DRIVER_ADDR_2
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_3
|
||||
# define IS31FL3741_I2C_ADDRESS_3 DRIVER_ADDR_3
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_4
|
||||
# define IS31FL3741_I2C_ADDRESS_4 DRIVER_ADDR_4
|
||||
#endif
|
||||
#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 +100,25 @@ 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
|
||||
void is31fl3741_flush(void);
|
||||
|
||||
#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
|
||||
|
||||
@@ -174,6 +174,19 @@ void IS31FL_common_update_scaling_register(uint8_t addr, uint8_t index) {
|
||||
}
|
||||
}
|
||||
|
||||
void IS31FL_common_flush(void) {
|
||||
IS31FL_common_update_pwm_register(DRIVER_ADDR_1, 0);
|
||||
#if defined(DRIVER_ADDR_2)
|
||||
IS31FL_common_update_pwm_register(DRIVER_ADDR_2, 1);
|
||||
# if defined(DRIVER_ADDR_3)
|
||||
IS31FL_common_update_pwm_register(DRIVER_ADDR_3, 2);
|
||||
# if defined(DRIVER_ADDR_4)
|
||||
IS31FL_common_update_pwm_register(DRIVER_ADDR_4, 3);
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef RGB_MATRIX_ENABLE
|
||||
// Colour is set by adjusting PWM register
|
||||
void IS31FL_RGB_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
|
||||
@@ -25,13 +25,13 @@
|
||||
#include "progmem.h"
|
||||
|
||||
// Which variant header file to use
|
||||
#ifdef IS31FL3742A
|
||||
#if defined(LED_MATRIX_IS31FL3742A) || defined(RGB_MATRIX_IS31FL3742A)
|
||||
# include "is31fl3742.h"
|
||||
#elif defined(IS31FL3743A)
|
||||
#elif defined(LED_MATRIX_IS31FL3743A) || defined(RGB_MATRIX_IS31FL3743A)
|
||||
# include "is31fl3743.h"
|
||||
#elif defined(IS31FL3745)
|
||||
#elif defined(LED_MATRIX_IS31FL3745) || defined(RGB_MATRIX_IS31FL3745)
|
||||
# include "is31fl3745.h"
|
||||
#elif defined(IS31FL3746A)
|
||||
#elif defined(LED_MATRIX_IS31FL3746A) || defined(RGB_MATRIX_IS31FL3746A)
|
||||
# include "is31fl3746.h"
|
||||
#endif
|
||||
|
||||
@@ -67,6 +67,8 @@ void IS31FL_common_init(uint8_t addr, uint8_t ssr);
|
||||
void IS31FL_common_update_pwm_register(uint8_t addr, uint8_t index);
|
||||
void IS31FL_common_update_scaling_register(uint8_t addr, uint8_t index);
|
||||
|
||||
void IS31FL_common_flush(void);
|
||||
|
||||
#ifdef RGB_MATRIX_ENABLE
|
||||
// RGB Matrix Specific scripts
|
||||
void IS31FL_RGB_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
|
||||
233
drivers/led/snled27351-simple.c
Normal file
233
drivers/led/snled27351-simple.c
Normal file
@@ -0,0 +1,233 @@
|
||||
/* 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 "snled27351-simple.h"
|
||||
#include "i2c_master.h"
|
||||
|
||||
#ifndef SNLED27351_I2C_TIMEOUT
|
||||
# define SNLED27351_I2C_TIMEOUT 100
|
||||
#endif
|
||||
|
||||
#ifndef SNLED27351_I2C_PERSISTENCE
|
||||
# define SNLED27351_I2C_PERSISTENCE 0
|
||||
#endif
|
||||
|
||||
#ifndef SNLED27351_PHASE_CHANNEL
|
||||
# define SNLED27351_PHASE_CHANNEL SNLED27351_MSKPHASE_12CHANNEL
|
||||
#endif
|
||||
|
||||
#ifndef SNLED27351_CURRENT_TUNE
|
||||
# define SNLED27351_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 SNLED27351 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 snled27351_write_pwm_buffer() but it's
|
||||
// probably not worth the extra complexity.
|
||||
uint8_t g_pwm_buffer[SNLED27351_DRIVER_COUNT][192];
|
||||
bool g_pwm_buffer_update_required[SNLED27351_DRIVER_COUNT] = {false};
|
||||
|
||||
uint8_t g_led_control_registers[SNLED27351_DRIVER_COUNT][24] = {0};
|
||||
bool g_led_control_registers_update_required[SNLED27351_DRIVER_COUNT] = {false};
|
||||
|
||||
bool snled27351_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 SNLED27351_I2C_PERSISTENCE > 0
|
||||
for (uint8_t i = 0; i < SNLED27351_I2C_PERSISTENCE; i++) {
|
||||
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, SNLED27351_I2C_TIMEOUT) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, SNLED27351_I2C_TIMEOUT) != 0) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool snled27351_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 SNLED27351_I2C_PERSISTENCE > 0
|
||||
for (uint8_t i = 0; i < SNLED27351_I2C_PERSISTENCE; i++) {
|
||||
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, SNLED27351_I2C_TIMEOUT) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, SNLED27351_I2C_TIMEOUT) != 0) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void snled27351_init(uint8_t addr) {
|
||||
// Select to function page
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_FUNCTION_PAGE);
|
||||
// Setting LED driver to shutdown mode
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURATION, SNLED27351_MSKSW_SHUT_DOWN_MODE);
|
||||
// Setting internal channel pulldown/pullup
|
||||
snled27351_write_register(addr, SNLED27351_REG_PDU, SNLED27351_MSKSET_CA_CB_CHANNEL);
|
||||
// Select number of scan phase
|
||||
snled27351_write_register(addr, SNLED27351_REG_SCAN_PHASE, SNLED27351_PHASE_CHANNEL);
|
||||
// Setting PWM Delay Phase
|
||||
snled27351_write_register(addr, SNLED27351_REG_SLEW_RATE_CONTROL_MODE1, SNLED27351_MSKPWM_DELAY_PHASE_ENABLE);
|
||||
// Setting Driving/Sinking Channel Slew Rate
|
||||
snled27351_write_register(addr, SNLED27351_REG_SLEW_RATE_CONTROL_MODE2, SNLED27351_MSKDRIVING_SINKING_CHANNEL_SLEWRATE_ENABLE);
|
||||
// Setting Iref
|
||||
snled27351_write_register(addr, SNLED27351_REG_SOFTWARE_SLEEP, SNLED27351_MSKSLEEP_DISABLE);
|
||||
// Set LED CONTROL PAGE (Page 0)
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_LED_CONTROL_PAGE);
|
||||
for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) {
|
||||
snled27351_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// Set PWM PAGE (Page 1)
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_LED_PWM_PAGE);
|
||||
for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) {
|
||||
snled27351_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// Set CURRENT PAGE (Page 4)
|
||||
uint8_t current_tune_reg_list[SNLED27351_LED_CURRENT_TUNE_LENGTH] = SNLED27351_CURRENT_TUNE;
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_CURRENT_TUNE_PAGE);
|
||||
for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) {
|
||||
snled27351_write_register(addr, i, current_tune_reg_list[i]);
|
||||
}
|
||||
|
||||
// Enable LEDs ON/OFF
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_LED_CONTROL_PAGE);
|
||||
for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) {
|
||||
snled27351_write_register(addr, i, 0xFF);
|
||||
}
|
||||
|
||||
// Select to function page
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_FUNCTION_PAGE);
|
||||
// Setting LED driver to normal mode
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURATION, SNLED27351_MSKSW_NORMAL_MODE);
|
||||
}
|
||||
|
||||
void snled27351_set_value(int index, uint8_t value) {
|
||||
snled27351_led_t led;
|
||||
if (index >= 0 && index < LED_MATRIX_LED_COUNT) {
|
||||
memcpy_P(&led, (&g_snled27351_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 snled27351_set_value_all(uint8_t value) {
|
||||
for (int i = 0; i < LED_MATRIX_LED_COUNT; i++) {
|
||||
snled27351_set_value(i, value);
|
||||
}
|
||||
}
|
||||
|
||||
void snled27351_set_led_control_register(uint8_t index, bool value) {
|
||||
snled27351_led_t led;
|
||||
memcpy_P(&led, (&g_snled27351_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 snled27351_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||
if (g_pwm_buffer_update_required[index]) {
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_LED_PWM_PAGE);
|
||||
|
||||
// If any of the transactions fail we risk writing dirty PG0,
|
||||
// refresh page 0 just in case.
|
||||
if (!snled27351_write_pwm_buffer(addr, g_pwm_buffer[index])) {
|
||||
g_led_control_registers_update_required[index] = true;
|
||||
}
|
||||
}
|
||||
g_pwm_buffer_update_required[index] = false;
|
||||
}
|
||||
|
||||
void snled27351_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
if (g_led_control_registers_update_required[index]) {
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_LED_CONTROL_PAGE);
|
||||
for (int i = 0; i < 24; i++) {
|
||||
snled27351_write_register(addr, i, g_led_control_registers[index][i]);
|
||||
}
|
||||
}
|
||||
g_led_control_registers_update_required[index] = false;
|
||||
}
|
||||
|
||||
void snled27351_flush(void) {
|
||||
snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_1, 0);
|
||||
#if defined(SNLED27351_I2C_ADDRESS_2)
|
||||
snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_2, 1);
|
||||
# if defined(SNLED27351_I2C_ADDRESS_3)
|
||||
snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_3, 2);
|
||||
# if defined(SNLED27351_I2C_ADDRESS_4)
|
||||
snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_4, 3);
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void snled27351_sw_return_normal(uint8_t addr) {
|
||||
// Select to function page
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_FUNCTION_PAGE);
|
||||
// Setting LED driver to normal mode
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURATION, SNLED27351_MSKSW_NORMAL_MODE);
|
||||
}
|
||||
|
||||
void snled27351_sw_shutdown(uint8_t addr) {
|
||||
// Select to function page
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_FUNCTION_PAGE);
|
||||
// Setting LED driver to shutdown mode
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURATION, SNLED27351_MSKSW_SHUT_DOWN_MODE);
|
||||
// Write SW Sleep Register
|
||||
snled27351_write_register(addr, SNLED27351_REG_SOFTWARE_SLEEP, SNLED27351_MSKSLEEP_ENABLE);
|
||||
}
|
||||
378
drivers/led/snled27351-simple.h
Normal file
378
drivers/led/snled27351-simple.h
Normal file
@@ -0,0 +1,378 @@
|
||||
/* 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"
|
||||
|
||||
// ======== DEPRECATED DEFINES - DO NOT USE ========
|
||||
#ifdef DRIVER_COUNT
|
||||
# define SNLED27351_DRIVER_COUNT DRIVER_COUNT
|
||||
#endif
|
||||
#ifdef CKLED2001_TIMEOUT
|
||||
# define SNLED27351_I2C_TIMEOUT CKLED2001_TIMEOUT
|
||||
#endif
|
||||
#ifdef CKLED2001_PERSISTENCE
|
||||
# define SNLED27351_I2C_PERSISTENCE CKLED2001_PERSISTENCE
|
||||
#endif
|
||||
#ifdef PHASE_CHANNEL
|
||||
# define SNLED27351_PHASE_CHANNEL PHASE_CHANNEL
|
||||
#endif
|
||||
#ifdef CKLED2001_CURRENT_TUNE
|
||||
# define SNLED27351_CURRENT_TUNE CKLED2001_CURRENT_TUNE
|
||||
#endif
|
||||
|
||||
#define MSKPHASE_12CHANNEL SNLED27351_MSKPHASE_12CHANNEL
|
||||
#define MSKPHASE_11CHANNEL SNLED27351_MSKPHASE_11CHANNEL
|
||||
#define MSKPHASE_10CHANNEL SNLED27351_MSKPHASE_10CHANNEL
|
||||
#define MSKPHASE_9CHANNEL SNLED27351_MSKPHASE_9CHANNEL
|
||||
#define MSKPHASE_8CHANNEL SNLED27351_MSKPHASE_8CHANNEL
|
||||
#define MSKPHASE_7CHANNEL SNLED27351_MSKPHASE_7CHANNEL
|
||||
#define MSKPHASE_6CHANNEL SNLED27351_MSKPHASE_6CHANNEL
|
||||
#define MSKPHASE_5CHANNEL SNLED27351_MSKPHASE_5CHANNEL
|
||||
#define MSKPHASE_4CHANNEL SNLED27351_MSKPHASE_4CHANNEL
|
||||
#define MSKPHASE_3CHANNEL SNLED27351_MSKPHASE_3CHANNEL
|
||||
#define MSKPHASE_2CHANNEL SNLED27351_MSKPHASE_2CHANNEL
|
||||
#define MSKPHASE_1CHANNEL SNLED27351_MSKPHASE_1CHANNEL
|
||||
|
||||
#define ckled2001_led snled27351_led_t
|
||||
#define g_ckled2001_leds g_snled27351_leds
|
||||
// ========
|
||||
|
||||
#define SNLED27351_I2C_ADDRESS_GND 0x74
|
||||
#define SNLED27351_I2C_ADDRESS_SCL 0x75
|
||||
#define SNLED27351_I2C_ADDRESS_SDA 0x76
|
||||
#define SNLED27351_I2C_ADDRESS_VDDIO 0x77
|
||||
|
||||
typedef struct snled27351_led_t {
|
||||
uint8_t driver : 2;
|
||||
uint8_t v;
|
||||
} __attribute__((packed)) snled27351_led_t;
|
||||
|
||||
extern const snled27351_led_t PROGMEM g_snled27351_leds[LED_MATRIX_LED_COUNT];
|
||||
|
||||
void snled27351_init(uint8_t addr);
|
||||
bool snled27351_write_register(uint8_t addr, uint8_t reg, uint8_t data);
|
||||
bool snled27351_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
|
||||
|
||||
void snled27351_set_value(int index, uint8_t value);
|
||||
void snled27351_set_value_all(uint8_t value);
|
||||
|
||||
void snled27351_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 snled27351_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void snled27351_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
|
||||
void snled27351_flush(void);
|
||||
|
||||
void snled27351_sw_return_normal(uint8_t addr);
|
||||
void snled27351_sw_shutdown(uint8_t addr);
|
||||
|
||||
// Registers Page Define
|
||||
#define SNLED27351_REG_CONFIGURE_CMD_PAGE 0xFD
|
||||
#define SNLED27351_LED_CONTROL_PAGE 0x00
|
||||
#define SNLED27351_LED_PWM_PAGE 0x01
|
||||
#define SNLED27351_FUNCTION_PAGE 0x03
|
||||
#define SNLED27351_CURRENT_TUNE_PAGE 0x04
|
||||
|
||||
// Function Register: address 0x00
|
||||
#define SNLED27351_REG_CONFIGURATION 0x00
|
||||
#define SNLED27351_MSKSW_SHUT_DOWN_MODE (0x0 << 0)
|
||||
#define SNLED27351_MSKSW_NORMAL_MODE (0x1 << 0)
|
||||
|
||||
#define SNLED27351_REG_DRIVER_ID 0x11
|
||||
#define SNLED27351_DRIVER_ID 0x8A
|
||||
|
||||
#define SNLED27351_REG_PDU 0x13
|
||||
#define SNLED27351_MSKSET_CA_CB_CHANNEL 0xAA
|
||||
#define SNLED27351_MSKCLR_CA_CB_CHANNEL 0x00
|
||||
|
||||
#define SNLED27351_REG_SCAN_PHASE 0x14
|
||||
#define SNLED27351_MSKPHASE_12CHANNEL 0x00
|
||||
#define SNLED27351_MSKPHASE_11CHANNEL 0x01
|
||||
#define SNLED27351_MSKPHASE_10CHANNEL 0x02
|
||||
#define SNLED27351_MSKPHASE_9CHANNEL 0x03
|
||||
#define SNLED27351_MSKPHASE_8CHANNEL 0x04
|
||||
#define SNLED27351_MSKPHASE_7CHANNEL 0x05
|
||||
#define SNLED27351_MSKPHASE_6CHANNEL 0x06
|
||||
#define SNLED27351_MSKPHASE_5CHANNEL 0x07
|
||||
#define SNLED27351_MSKPHASE_4CHANNEL 0x08
|
||||
#define SNLED27351_MSKPHASE_3CHANNEL 0x09
|
||||
#define SNLED27351_MSKPHASE_2CHANNEL 0x0A
|
||||
#define SNLED27351_MSKPHASE_1CHANNEL 0x0B
|
||||
|
||||
#define SNLED27351_REG_SLEW_RATE_CONTROL_MODE1 0x15
|
||||
#define SNLED27351_MSKPWM_DELAY_PHASE_ENABLE 0x04
|
||||
#define SNLED27351_MSKPWM_DELAY_PHASE_DISABLE 0x00
|
||||
|
||||
#define SNLED27351_REG_SLEW_RATE_CONTROL_MODE2 0x16
|
||||
#define SNLED27351_MSKDRIVING_SINKING_CHANNEL_SLEWRATE_ENABLE 0xC0
|
||||
#define SNLED27351_MSKDRIVING_SINKING_CHANNEL_SLEWRATE_DISABLE 0x00
|
||||
|
||||
#define SNLED27351_REG_OPEN_SHORT_ENABLE 0x17
|
||||
#define SNLED27351_MSKOPEN_DETECTION_ENABLE (0x01 << 7)
|
||||
#define SNLED27351_MSKOPEN_DETECTION_DISABLE (0x00)
|
||||
|
||||
#define SNLED27351_MSKSHORT_DETECTION_ENABLE (0x01 << 6)
|
||||
#define SNLED27351_MSKSHORT_DETECTION_DISABLE (0x00)
|
||||
|
||||
#define SNLED27351_REG_OPEN_SHORT_DUTY 0x18
|
||||
#define SNLED27351_REG_OPEN_SHORT_FLAG 0x19
|
||||
|
||||
#define SNLED27351_MSKOPEN_DETECTION_INTERRUPT_ENABLE (0x01 << 7)
|
||||
#define SNLED27351_MSKOPEN_DETECTION_INTERRUPT_DISABLE (0x00)
|
||||
|
||||
#define SNLED27351_MSKSHORT_DETECTION_INTERRUPT_ENABLE (0x01 << 6)
|
||||
#define SNLED27351_MSKSHORT_DETECTION_INTERRUPT_DISABLE (0x00)
|
||||
|
||||
#define SNLED27351_REG_SOFTWARE_SLEEP 0x1A
|
||||
#define SNLED27351_MSKSLEEP_ENABLE 0x02
|
||||
#define SNLED27351_MSKSLEEP_DISABLE 0x00
|
||||
|
||||
// LED Control Registers
|
||||
#define SNLED27351_LED_CONTROL_ON_OFF_FIRST_ADDR 0x0
|
||||
#define SNLED27351_LED_CONTROL_ON_OFF_LAST_ADDR 0x17
|
||||
#define SNLED27351_LED_CONTROL_ON_OFF_LENGTH ((SNLED27351_LED_CONTROL_ON_OFF_LAST_ADDR - SNLED27351_LED_CONTROL_ON_OFF_FIRST_ADDR) + 1)
|
||||
|
||||
#define SNLED27351_LED_CONTROL_OPEN_FIRST_ADDR 0x18
|
||||
#define SNLED27351_LED_CONTROL_OPEN_LAST_ADDR 0x2F
|
||||
#define SNLED27351_LED_CONTROL_OPEN_LENGTH ((SNLED27351_LED_CONTROL_OPEN_LAST_ADDR - SNLED27351_LED_CONTROL_OPEN_FIRST_ADDR) + 1)
|
||||
|
||||
#define SNLED27351_LED_CONTROL_SHORT_FIRST_ADDR 0x30
|
||||
#define SNLED27351_LED_CONTROL_SHORT_LAST_ADDR 0x47
|
||||
#define SNLED27351_LED_CONTROL_SHORT_LENGTH ((SNLED27351_LED_CONTROL_SHORT_LAST_ADDR - SNLED27351_LED_CONTROL_SHORT_FIRST_ADDR) + 1)
|
||||
|
||||
#define SNLED27351_LED_CONTROL_PAGE_LENGTH 0x48
|
||||
|
||||
// LED Control Registers
|
||||
#define SNLED27351_LED_PWM_FIRST_ADDR 0x00
|
||||
#define SNLED27351_LED_PWM_LAST_ADDR 0xBF
|
||||
#define SNLED27351_LED_PWM_LENGTH 0xC0
|
||||
|
||||
// Current Tune Registers
|
||||
#define SNLED27351_LED_CURRENT_TUNE_FIRST_ADDR 0x00
|
||||
#define SNLED27351_LED_CURRENT_TUNE_LAST_ADDR 0x0B
|
||||
#define SNLED27351_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
|
||||
248
drivers/led/snled27351.c
Normal file
248
drivers/led/snled27351.c
Normal file
@@ -0,0 +1,248 @@
|
||||
/* 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 "snled27351.h"
|
||||
#include "i2c_master.h"
|
||||
|
||||
#ifndef SNLED27351_I2C_TIMEOUT
|
||||
# define SNLED27351_I2C_TIMEOUT 100
|
||||
#endif
|
||||
|
||||
#ifndef SNLED27351_I2C_PERSISTENCE
|
||||
# define SNLED27351_I2C_PERSISTENCE 0
|
||||
#endif
|
||||
|
||||
#ifndef SNLED27351_PHASE_CHANNEL
|
||||
# define SNLED27351_PHASE_CHANNEL SNLED27351_MSKPHASE_12CHANNEL
|
||||
#endif
|
||||
|
||||
#ifndef SNLED27351_CURRENT_TUNE
|
||||
# define SNLED27351_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 SNLED27351 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 snled27351_write_pwm_buffer() but it's
|
||||
// probably not worth the extra complexity.
|
||||
uint8_t g_pwm_buffer[SNLED27351_DRIVER_COUNT][192];
|
||||
bool g_pwm_buffer_update_required[SNLED27351_DRIVER_COUNT] = {false};
|
||||
|
||||
uint8_t g_led_control_registers[SNLED27351_DRIVER_COUNT][24] = {0};
|
||||
bool g_led_control_registers_update_required[SNLED27351_DRIVER_COUNT] = {false};
|
||||
|
||||
bool snled27351_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 SNLED27351_I2C_PERSISTENCE > 0
|
||||
for (uint8_t i = 0; i < SNLED27351_I2C_PERSISTENCE; i++) {
|
||||
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, SNLED27351_I2C_TIMEOUT) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, SNLED27351_I2C_TIMEOUT) != 0) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool snled27351_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 SNLED27351_I2C_PERSISTENCE > 0
|
||||
for (uint8_t i = 0; i < SNLED27351_I2C_PERSISTENCE; i++) {
|
||||
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 65, SNLED27351_I2C_TIMEOUT) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 65, SNLED27351_I2C_TIMEOUT) != 0) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void snled27351_init(uint8_t addr) {
|
||||
// Select to function page
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_FUNCTION_PAGE);
|
||||
// Setting LED driver to shutdown mode
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURATION, SNLED27351_MSKSW_SHUT_DOWN_MODE);
|
||||
// Setting internal channel pulldown/pullup
|
||||
snled27351_write_register(addr, SNLED27351_REG_PDU, SNLED27351_MSKSET_CA_CB_CHANNEL);
|
||||
// Select number of scan phase
|
||||
snled27351_write_register(addr, SNLED27351_REG_SCAN_PHASE, SNLED27351_PHASE_CHANNEL);
|
||||
// Setting PWM Delay Phase
|
||||
snled27351_write_register(addr, SNLED27351_REG_SLEW_RATE_CONTROL_MODE1, SNLED27351_MSKPWM_DELAY_PHASE_ENABLE);
|
||||
// Setting Driving/Sinking Channel Slew Rate
|
||||
snled27351_write_register(addr, SNLED27351_REG_SLEW_RATE_CONTROL_MODE2, SNLED27351_MSKDRIVING_SINKING_CHANNEL_SLEWRATE_ENABLE);
|
||||
// Setting Iref
|
||||
snled27351_write_register(addr, SNLED27351_REG_SOFTWARE_SLEEP, SNLED27351_MSKSLEEP_DISABLE);
|
||||
// Set LED CONTROL PAGE (Page 0)
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_LED_CONTROL_PAGE);
|
||||
for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) {
|
||||
snled27351_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// Set PWM PAGE (Page 1)
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_LED_PWM_PAGE);
|
||||
for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) {
|
||||
snled27351_write_register(addr, i, 0x00);
|
||||
}
|
||||
|
||||
// Set CURRENT PAGE (Page 4)
|
||||
uint8_t current_tune_reg_list[SNLED27351_LED_CURRENT_TUNE_LENGTH] = SNLED27351_CURRENT_TUNE;
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_CURRENT_TUNE_PAGE);
|
||||
for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) {
|
||||
snled27351_write_register(addr, i, current_tune_reg_list[i]);
|
||||
}
|
||||
|
||||
// Enable LEDs ON/OFF
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_LED_CONTROL_PAGE);
|
||||
for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) {
|
||||
snled27351_write_register(addr, i, 0xFF);
|
||||
}
|
||||
|
||||
// Select to function page
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_FUNCTION_PAGE);
|
||||
// Setting LED driver to normal mode
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURATION, SNLED27351_MSKSW_NORMAL_MODE);
|
||||
}
|
||||
|
||||
void snled27351_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
snled27351_led_t led;
|
||||
if (index >= 0 && index < RGB_MATRIX_LED_COUNT) {
|
||||
memcpy_P(&led, (&g_snled27351_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 snled27351_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
for (int i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
|
||||
snled27351_set_color(i, red, green, blue);
|
||||
}
|
||||
}
|
||||
|
||||
void snled27351_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {
|
||||
snled27351_led_t led;
|
||||
memcpy_P(&led, (&g_snled27351_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 snled27351_update_pwm_buffers(uint8_t addr, uint8_t index) {
|
||||
if (g_pwm_buffer_update_required[index]) {
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_LED_PWM_PAGE);
|
||||
|
||||
// If any of the transactions fail we risk writing dirty PG0,
|
||||
// refresh page 0 just in case.
|
||||
if (!snled27351_write_pwm_buffer(addr, g_pwm_buffer[index])) {
|
||||
g_led_control_registers_update_required[index] = true;
|
||||
}
|
||||
}
|
||||
g_pwm_buffer_update_required[index] = false;
|
||||
}
|
||||
|
||||
void snled27351_update_led_control_registers(uint8_t addr, uint8_t index) {
|
||||
if (g_led_control_registers_update_required[index]) {
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_LED_CONTROL_PAGE);
|
||||
for (int i = 0; i < 24; i++) {
|
||||
snled27351_write_register(addr, i, g_led_control_registers[index][i]);
|
||||
}
|
||||
}
|
||||
g_led_control_registers_update_required[index] = false;
|
||||
}
|
||||
|
||||
void snled27351_flush(void) {
|
||||
snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_1, 0);
|
||||
#if defined(SNLED27351_I2C_ADDRESS_2)
|
||||
snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_2, 1);
|
||||
# if defined(SNLED27351_I2C_ADDRESS_3)
|
||||
snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_3, 2);
|
||||
# if defined(SNLED27351_I2C_ADDRESS_4)
|
||||
snled27351_update_pwm_buffers(SNLED27351_I2C_ADDRESS_4, 3);
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void snled27351_sw_return_normal(uint8_t addr) {
|
||||
// Select to function page
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_FUNCTION_PAGE);
|
||||
// Setting LED driver to normal mode
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURATION, SNLED27351_MSKSW_NORMAL_MODE);
|
||||
}
|
||||
|
||||
void snled27351_sw_shutdown(uint8_t addr) {
|
||||
// Select to function page
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURE_CMD_PAGE, SNLED27351_FUNCTION_PAGE);
|
||||
// Setting LED driver to shutdown mode
|
||||
snled27351_write_register(addr, SNLED27351_REG_CONFIGURATION, SNLED27351_MSKSW_SHUT_DOWN_MODE);
|
||||
// Write SW Sleep Register
|
||||
snled27351_write_register(addr, SNLED27351_REG_SOFTWARE_SLEEP, SNLED27351_MSKSLEEP_ENABLE);
|
||||
}
|
||||
392
drivers/led/snled27351.h
Normal file
392
drivers/led/snled27351.h
Normal file
@@ -0,0 +1,392 @@
|
||||
/* 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"
|
||||
|
||||
// ======== DEPRECATED DEFINES - DO NOT USE ========
|
||||
#ifdef DRIVER_COUNT
|
||||
# define SNLED27351_DRIVER_COUNT DRIVER_COUNT
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_1
|
||||
# define SNLED27351_I2C_ADDRESS_1 DRIVER_ADDR_1
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_2
|
||||
# define SNLED27351_I2C_ADDRESS_2 DRIVER_ADDR_2
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_3
|
||||
# define SNLED27351_I2C_ADDRESS_3 DRIVER_ADDR_3
|
||||
#endif
|
||||
#ifdef DRIVER_ADDR_4
|
||||
# define SNLED27351_I2C_ADDRESS_4 DRIVER_ADDR_4
|
||||
#endif
|
||||
#ifdef CKLED2001_TIMEOUT
|
||||
# define SNLED27351_I2C_TIMEOUT CKLED2001_TIMEOUT
|
||||
#endif
|
||||
#ifdef CKLED2001_PERSISTENCE
|
||||
# define SNLED27351_I2C_PERSISTENCE CKLED2001_PERSISTENCE
|
||||
#endif
|
||||
#ifdef PHASE_CHANNEL
|
||||
# define SNLED27351_PHASE_CHANNEL PHASE_CHANNEL
|
||||
#endif
|
||||
#ifdef CKLED2001_CURRENT_TUNE
|
||||
# define SNLED27351_CURRENT_TUNE CKLED2001_CURRENT_TUNE
|
||||
#endif
|
||||
|
||||
#define MSKPHASE_12CHANNEL SNLED27351_MSKPHASE_12CHANNEL
|
||||
#define MSKPHASE_11CHANNEL SNLED27351_MSKPHASE_11CHANNEL
|
||||
#define MSKPHASE_10CHANNEL SNLED27351_MSKPHASE_10CHANNEL
|
||||
#define MSKPHASE_9CHANNEL SNLED27351_MSKPHASE_9CHANNEL
|
||||
#define MSKPHASE_8CHANNEL SNLED27351_MSKPHASE_8CHANNEL
|
||||
#define MSKPHASE_7CHANNEL SNLED27351_MSKPHASE_7CHANNEL
|
||||
#define MSKPHASE_6CHANNEL SNLED27351_MSKPHASE_6CHANNEL
|
||||
#define MSKPHASE_5CHANNEL SNLED27351_MSKPHASE_5CHANNEL
|
||||
#define MSKPHASE_4CHANNEL SNLED27351_MSKPHASE_4CHANNEL
|
||||
#define MSKPHASE_3CHANNEL SNLED27351_MSKPHASE_3CHANNEL
|
||||
#define MSKPHASE_2CHANNEL SNLED27351_MSKPHASE_2CHANNEL
|
||||
#define MSKPHASE_1CHANNEL SNLED27351_MSKPHASE_1CHANNEL
|
||||
|
||||
#define ckled2001_led snled27351_led_t
|
||||
#define g_ckled2001_leds g_snled27351_leds
|
||||
// ========
|
||||
|
||||
#define SNLED27351_I2C_ADDRESS_GND 0x74
|
||||
#define SNLED27351_I2C_ADDRESS_SCL 0x75
|
||||
#define SNLED27351_I2C_ADDRESS_SDA 0x76
|
||||
#define SNLED27351_I2C_ADDRESS_VDDIO 0x77
|
||||
|
||||
typedef struct snled27351_led_t {
|
||||
uint8_t driver : 2;
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
} __attribute__((packed)) snled27351_led_t;
|
||||
|
||||
extern const snled27351_led_t PROGMEM g_snled27351_leds[RGB_MATRIX_LED_COUNT];
|
||||
|
||||
void snled27351_init(uint8_t addr);
|
||||
bool snled27351_write_register(uint8_t addr, uint8_t reg, uint8_t data);
|
||||
bool snled27351_write_pwm_buffer(uint8_t addr, uint8_t *pwm_buffer);
|
||||
|
||||
void snled27351_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
|
||||
void snled27351_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
|
||||
|
||||
void snled27351_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 snled27351_update_pwm_buffers(uint8_t addr, uint8_t index);
|
||||
void snled27351_update_led_control_registers(uint8_t addr, uint8_t index);
|
||||
|
||||
void snled27351_flush(void);
|
||||
|
||||
void snled27351_sw_return_normal(uint8_t addr);
|
||||
void snled27351_sw_shutdown(uint8_t addr);
|
||||
|
||||
// Registers Page Define
|
||||
#define SNLED27351_REG_CONFIGURE_CMD_PAGE 0xFD
|
||||
#define SNLED27351_LED_CONTROL_PAGE 0x00
|
||||
#define SNLED27351_LED_PWM_PAGE 0x01
|
||||
#define SNLED27351_FUNCTION_PAGE 0x03
|
||||
#define SNLED27351_CURRENT_TUNE_PAGE 0x04
|
||||
|
||||
// Function Register: address 0x00
|
||||
#define SNLED27351_REG_CONFIGURATION 0x00
|
||||
#define SNLED27351_MSKSW_SHUT_DOWN_MODE (0x0 << 0)
|
||||
#define SNLED27351_MSKSW_NORMAL_MODE (0x1 << 0)
|
||||
|
||||
#define SNLED27351_REG_DRIVER_ID 0x11
|
||||
#define SNLED27351_DRIVER_ID 0x8A
|
||||
|
||||
#define SNLED27351_REG_PDU 0x13
|
||||
#define SNLED27351_MSKSET_CA_CB_CHANNEL 0xAA
|
||||
#define SNLED27351_MSKCLR_CA_CB_CHANNEL 0x00
|
||||
|
||||
#define SNLED27351_REG_SCAN_PHASE 0x14
|
||||
#define SNLED27351_MSKPHASE_12CHANNEL 0x00
|
||||
#define SNLED27351_MSKPHASE_11CHANNEL 0x01
|
||||
#define SNLED27351_MSKPHASE_10CHANNEL 0x02
|
||||
#define SNLED27351_MSKPHASE_9CHANNEL 0x03
|
||||
#define SNLED27351_MSKPHASE_8CHANNEL 0x04
|
||||
#define SNLED27351_MSKPHASE_7CHANNEL 0x05
|
||||
#define SNLED27351_MSKPHASE_6CHANNEL 0x06
|
||||
#define SNLED27351_MSKPHASE_5CHANNEL 0x07
|
||||
#define SNLED27351_MSKPHASE_4CHANNEL 0x08
|
||||
#define SNLED27351_MSKPHASE_3CHANNEL 0x09
|
||||
#define SNLED27351_MSKPHASE_2CHANNEL 0x0A
|
||||
#define SNLED27351_MSKPHASE_1CHANNEL 0x0B
|
||||
|
||||
#define SNLED27351_REG_SLEW_RATE_CONTROL_MODE1 0x15
|
||||
#define SNLED27351_MSKPWM_DELAY_PHASE_ENABLE 0x04
|
||||
#define SNLED27351_MSKPWM_DELAY_PHASE_DISABLE 0x00
|
||||
|
||||
#define SNLED27351_REG_SLEW_RATE_CONTROL_MODE2 0x16
|
||||
#define SNLED27351_MSKDRIVING_SINKING_CHANNEL_SLEWRATE_ENABLE 0xC0
|
||||
#define SNLED27351_MSKDRIVING_SINKING_CHANNEL_SLEWRATE_DISABLE 0x00
|
||||
|
||||
#define SNLED27351_REG_OPEN_SHORT_ENABLE 0x17
|
||||
#define SNLED27351_MSKOPEN_DETECTION_ENABLE (0x01 << 7)
|
||||
#define SNLED27351_MSKOPEN_DETECTION_DISABLE (0x00)
|
||||
|
||||
#define SNLED27351_MSKSHORT_DETECTION_ENABLE (0x01 << 6)
|
||||
#define SNLED27351_MSKSHORT_DETECTION_DISABLE (0x00)
|
||||
|
||||
#define SNLED27351_REG_OPEN_SHORT_DUTY 0x18
|
||||
#define SNLED27351_REG_OPEN_SHORT_FLAG 0x19
|
||||
|
||||
#define SNLED27351_MSKOPEN_DETECTION_INTERRUPT_ENABLE (0x01 << 7)
|
||||
#define SNLED27351_MSKOPEN_DETECTION_INTERRUPT_DISABLE (0x00)
|
||||
|
||||
#define SNLED27351_MSKSHORT_DETECTION_INTERRUPT_ENABLE (0x01 << 6)
|
||||
#define SNLED27351_MSKSHORT_DETECTION_INTERRUPT_DISABLE (0x00)
|
||||
|
||||
#define SNLED27351_REG_SOFTWARE_SLEEP 0x1A
|
||||
#define SNLED27351_MSKSLEEP_ENABLE 0x02
|
||||
#define SNLED27351_MSKSLEEP_DISABLE 0x00
|
||||
|
||||
// LED Control Registers
|
||||
#define SNLED27351_LED_CONTROL_ON_OFF_FIRST_ADDR 0x0
|
||||
#define SNLED27351_LED_CONTROL_ON_OFF_LAST_ADDR 0x17
|
||||
#define SNLED27351_LED_CONTROL_ON_OFF_LENGTH ((SNLED27351_LED_CONTROL_ON_OFF_LAST_ADDR - SNLED27351_LED_CONTROL_ON_OFF_FIRST_ADDR) + 1)
|
||||
|
||||
#define SNLED27351_LED_CONTROL_OPEN_FIRST_ADDR 0x18
|
||||
#define SNLED27351_LED_CONTROL_OPEN_LAST_ADDR 0x2F
|
||||
#define SNLED27351_LED_CONTROL_OPEN_LENGTH ((SNLED27351_LED_CONTROL_OPEN_LAST_ADDR - SNLED27351_LED_CONTROL_OPEN_FIRST_ADDR) + 1)
|
||||
|
||||
#define SNLED27351_LED_CONTROL_SHORT_FIRST_ADDR 0x30
|
||||
#define SNLED27351_LED_CONTROL_SHORT_LAST_ADDR 0x47
|
||||
#define SNLED27351_LED_CONTROL_SHORT_LENGTH ((SNLED27351_LED_CONTROL_SHORT_LAST_ADDR - SNLED27351_LED_CONTROL_SHORT_FIRST_ADDR) + 1)
|
||||
|
||||
#define SNLED27351_LED_CONTROL_PAGE_LENGTH 0x48
|
||||
|
||||
// LED Control Registers
|
||||
#define SNLED27351_LED_PWM_FIRST_ADDR 0x00
|
||||
#define SNLED27351_LED_PWM_LAST_ADDR 0xBF
|
||||
#define SNLED27351_LED_PWM_LENGTH 0xC0
|
||||
|
||||
// Current Tune Registers
|
||||
#define SNLED27351_LED_CURRENT_TUNE_FIRST_ADDR 0x00
|
||||
#define SNLED27351_LED_CURRENT_TUNE_LAST_ADDR 0x0B
|
||||
#define SNLED27351_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
|
||||
@@ -451,7 +451,7 @@ static void rotate_90(const uint8_t *src, uint8_t *dest) {
|
||||
}
|
||||
}
|
||||
|
||||
void oled_render(void) {
|
||||
void oled_render_dirty(bool all) {
|
||||
// Do we have work to do?
|
||||
oled_dirty &= OLED_ALL_BLOCKS_MASK;
|
||||
if (!oled_dirty || !oled_initialized || oled_scrolling) {
|
||||
@@ -463,7 +463,7 @@ void oled_render(void) {
|
||||
|
||||
uint8_t update_start = 0;
|
||||
uint8_t num_processed = 0;
|
||||
while (oled_dirty && num_processed++ < OLED_UPDATE_PROCESS_LIMIT) { // render all dirty blocks (up to the configured limit)
|
||||
while (oled_dirty && (num_processed++ < OLED_UPDATE_PROCESS_LIMIT || all)) { // render all dirty blocks (up to the configured limit)
|
||||
// Find next dirty block
|
||||
while (!(oled_dirty & ((OLED_BLOCK_TYPE)1 << update_start))) {
|
||||
++update_start;
|
||||
|
||||
@@ -353,20 +353,24 @@ oled_rotation_t oled_init_user(oled_rotation_t rotation);
|
||||
// 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
|
||||
void oled_set_cursor(uint8_t col, uint8_t line);
|
||||
|
||||
// Advances the cursor to the next page, writing ' ' if true
|
||||
// Wraps to the begining when out of bounds
|
||||
// Wraps to the beginning when out of bounds
|
||||
void oled_advance_page(bool clearPageRemainder);
|
||||
|
||||
// Moves the cursor forward 1 character length
|
||||
// Advance page if there is not enough room for the next character
|
||||
// Wraps to the begining when out of bounds
|
||||
// Wraps to the beginning when out of bounds
|
||||
void oled_advance_char(void);
|
||||
|
||||
// Writes a single character to the buffer at current cursor position
|
||||
@@ -433,10 +437,10 @@ bool oled_off(void);
|
||||
// not
|
||||
bool is_oled_on(void);
|
||||
|
||||
// Sets the brightness of the display
|
||||
// Sets the brightness level of the display
|
||||
uint8_t oled_set_brightness(uint8_t level);
|
||||
|
||||
// Gets the current brightness of the display
|
||||
// Gets the current brightness level of the display
|
||||
uint8_t oled_get_brightness(void);
|
||||
|
||||
// Basically it's oled_render, but with timeout management and oled_task_user calling!
|
||||
@@ -454,16 +458,16 @@ 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);
|
||||
|
||||
// Scrolls the entire display right
|
||||
// Begin scrolling the entire display right
|
||||
// Returns true if the screen was scrolling or starts scrolling
|
||||
// NOTE: display contents cannot be changed while scrolling
|
||||
bool oled_scroll_right(void);
|
||||
|
||||
// Scrolls the entire display left
|
||||
// Begin scrolling the entire display left
|
||||
// Returns true if the screen was scrolling or starts scrolling
|
||||
// NOTE: display contents cannot be changed while scrolling
|
||||
bool oled_scroll_left(void);
|
||||
|
||||
34
drivers/painter/comms/qp_comms_dummy.c
Normal file
34
drivers/painter/comms/qp_comms_dummy.c
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2023 Nick Brassel (@tzarc)
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef QUANTUM_PAINTER_DUMMY_COMMS_ENABLE
|
||||
|
||||
# include "qp_comms_dummy.h"
|
||||
|
||||
static bool dummy_comms_init(painter_device_t device) {
|
||||
// No-op.
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dummy_comms_start(painter_device_t device) {
|
||||
// No-op.
|
||||
return true;
|
||||
}
|
||||
|
||||
static void dummy_comms_stop(painter_device_t device) {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
uint32_t dummy_comms_send(painter_device_t device, const void *data, uint32_t byte_count) {
|
||||
// No-op.
|
||||
return byte_count;
|
||||
}
|
||||
|
||||
painter_comms_vtable_t dummy_comms_vtable = {
|
||||
// These are all effective no-op's because they're not actually needed.
|
||||
.comms_init = dummy_comms_init,
|
||||
.comms_start = dummy_comms_start,
|
||||
.comms_stop = dummy_comms_stop,
|
||||
.comms_send = dummy_comms_send};
|
||||
|
||||
#endif // QUANTUM_PAINTER_DUMMY_COMMS_ENABLE
|
||||
11
drivers/painter/comms/qp_comms_dummy.h
Normal file
11
drivers/painter/comms/qp_comms_dummy.h
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2023 Nick Brassel (@tzarc)
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#pragma once
|
||||
|
||||
#ifdef QUANTUM_PAINTER_DUMMY_COMMS_ENABLE
|
||||
|
||||
# include "qp_internal.h"
|
||||
|
||||
extern painter_comms_vtable_t dummy_comms_vtable;
|
||||
|
||||
#endif // QUANTUM_PAINTER_DUMMY_COMMS_ENABLE
|
||||
94
drivers/painter/comms/qp_comms_i2c.c
Normal file
94
drivers/painter/comms/qp_comms_i2c.c
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright 2022 Nick Brassel (@tzarc)
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef QUANTUM_PAINTER_I2C_ENABLE
|
||||
|
||||
# include "i2c_master.h"
|
||||
# include "qp_comms_i2c.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Helpers
|
||||
|
||||
static uint32_t qp_comms_i2c_send_raw(painter_device_t device, const void *data, uint32_t byte_count) {
|
||||
painter_driver_t * driver = (painter_driver_t *)device;
|
||||
qp_comms_i2c_config_t *comms_config = (qp_comms_i2c_config_t *)driver->comms_config;
|
||||
i2c_status_t res = i2c_transmit(comms_config->chip_address << 1, data, byte_count, I2C_TIMEOUT);
|
||||
if (res < 0) {
|
||||
return 0;
|
||||
}
|
||||
return byte_count;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Base I2C support
|
||||
|
||||
bool qp_comms_i2c_init(painter_device_t device) {
|
||||
i2c_init();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool qp_comms_i2c_start(painter_device_t device) {
|
||||
painter_driver_t * driver = (painter_driver_t *)device;
|
||||
qp_comms_i2c_config_t *comms_config = (qp_comms_i2c_config_t *)driver->comms_config;
|
||||
return i2c_start(comms_config->chip_address << 1) == I2C_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t qp_comms_i2c_send_data(painter_device_t device, const void *data, uint32_t byte_count) {
|
||||
return qp_comms_i2c_send_raw(device, data, byte_count);
|
||||
}
|
||||
|
||||
void qp_comms_i2c_stop(painter_device_t device) {
|
||||
i2c_stop();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Command+Data I2C support
|
||||
|
||||
static const uint8_t cmd_byte = 0x00;
|
||||
static const uint8_t data_byte = 0x40;
|
||||
|
||||
void qp_comms_i2c_cmddata_send_command(painter_device_t device, uint8_t cmd) {
|
||||
uint8_t buf[2] = {cmd_byte, cmd};
|
||||
qp_comms_i2c_send_raw(device, &buf, 2);
|
||||
}
|
||||
|
||||
uint32_t qp_comms_i2c_cmddata_send_data(painter_device_t device, const void *data, uint32_t byte_count) {
|
||||
uint8_t buf[1 + byte_count];
|
||||
buf[0] = data_byte;
|
||||
memcpy(&buf[1], data, byte_count);
|
||||
if (qp_comms_i2c_send_raw(device, buf, sizeof(buf)) != sizeof(buf)) {
|
||||
return 0;
|
||||
}
|
||||
return byte_count;
|
||||
}
|
||||
|
||||
void qp_comms_i2c_bulk_command_sequence(painter_device_t device, const uint8_t *sequence, size_t sequence_len) {
|
||||
uint8_t buf[32];
|
||||
for (size_t i = 0; i < sequence_len;) {
|
||||
uint8_t command = sequence[i];
|
||||
uint8_t delay = sequence[i + 1];
|
||||
uint8_t num_bytes = sequence[i + 2];
|
||||
buf[0] = cmd_byte;
|
||||
buf[1] = command;
|
||||
memcpy(&buf[2], &sequence[i + 3], num_bytes);
|
||||
qp_comms_i2c_send_raw(device, buf, num_bytes + 2);
|
||||
if (delay > 0) {
|
||||
wait_ms(delay);
|
||||
}
|
||||
i += (3 + num_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
const painter_comms_with_command_vtable_t i2c_comms_cmddata_vtable = {
|
||||
.base =
|
||||
{
|
||||
.comms_init = qp_comms_i2c_init,
|
||||
.comms_start = qp_comms_i2c_start,
|
||||
.comms_send = qp_comms_i2c_cmddata_send_data,
|
||||
.comms_stop = qp_comms_i2c_stop,
|
||||
},
|
||||
.send_command = qp_comms_i2c_cmddata_send_command,
|
||||
.bulk_command_sequence = qp_comms_i2c_bulk_command_sequence,
|
||||
};
|
||||
|
||||
#endif // QUANTUM_PAINTER_I2C_ENABLE
|
||||
28
drivers/painter/comms/qp_comms_i2c.h
Normal file
28
drivers/painter/comms/qp_comms_i2c.h
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2022 Nick Brassel (@tzarc)
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#pragma once
|
||||
|
||||
#ifdef QUANTUM_PAINTER_I2C_ENABLE
|
||||
|
||||
# include <stdint.h>
|
||||
|
||||
# include "gpio.h"
|
||||
# include "qp_internal.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Base I2C support
|
||||
|
||||
typedef struct qp_comms_i2c_config_t {
|
||||
uint8_t chip_address;
|
||||
} qp_comms_i2c_config_t;
|
||||
|
||||
bool qp_comms_i2c_init(painter_device_t device);
|
||||
bool qp_comms_i2c_start(painter_device_t device);
|
||||
uint32_t qp_comms_i2c_send_data(painter_device_t device, const void* data, uint32_t byte_count);
|
||||
void qp_comms_i2c_stop(painter_device_t device);
|
||||
|
||||
extern const painter_comms_with_command_vtable_t i2c_comms_cmddata_vtable;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // QUANTUM_PAINTER_I2C_ENABLE
|
||||
@@ -105,13 +105,21 @@ void qp_comms_spi_dc_reset_send_command(painter_device_t device, uint8_t cmd) {
|
||||
}
|
||||
|
||||
void qp_comms_spi_dc_reset_bulk_command_sequence(painter_device_t device, const uint8_t *sequence, size_t sequence_len) {
|
||||
painter_driver_t * driver = (painter_driver_t *)device;
|
||||
qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config;
|
||||
for (size_t i = 0; i < sequence_len;) {
|
||||
uint8_t command = sequence[i];
|
||||
uint8_t delay = sequence[i + 1];
|
||||
uint8_t num_bytes = sequence[i + 2];
|
||||
qp_comms_spi_dc_reset_send_command(device, command);
|
||||
if (num_bytes > 0) {
|
||||
qp_comms_spi_dc_reset_send_data(device, &sequence[i + 3], num_bytes);
|
||||
if (comms_config->command_params_uses_command_pin) {
|
||||
for (uint8_t j = 0; j < num_bytes; j++) {
|
||||
qp_comms_spi_dc_reset_send_command(device, sequence[i + 3 + j]);
|
||||
}
|
||||
} else {
|
||||
qp_comms_spi_dc_reset_send_data(device, &sequence[i + 3], num_bytes);
|
||||
}
|
||||
}
|
||||
if (delay > 0) {
|
||||
wait_ms(delay);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright 2021 Nick Brassel (@tzarc)
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef QUANTUM_PAINTER_SPI_ENABLE
|
||||
@@ -36,6 +35,7 @@ typedef struct qp_comms_spi_dc_reset_config_t {
|
||||
qp_comms_spi_config_t spi_config;
|
||||
pin_t dc_pin;
|
||||
pin_t reset_pin;
|
||||
bool command_params_uses_command_pin; // keep D/C held low when sending command sequences for data bytes
|
||||
} qp_comms_spi_dc_reset_config_t;
|
||||
|
||||
void qp_comms_spi_dc_reset_send_command(painter_device_t device, uint8_t cmd);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Copyright 2023 Nick Brassel (@tzarc)
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <wait.h>
|
||||
#include "qp_internal.h"
|
||||
#include "qp_comms.h"
|
||||
#include "qp_gc9a01.h"
|
||||
@@ -135,13 +134,14 @@ painter_device_t qp_gc9a01_make_spi_device(uint16_t panel_width, uint16_t panel_
|
||||
driver->base.offset_y = 0;
|
||||
|
||||
// SPI and other pin configuration
|
||||
driver->base.comms_config = &driver->spi_dc_reset_config;
|
||||
driver->spi_dc_reset_config.spi_config.chip_select_pin = chip_select_pin;
|
||||
driver->spi_dc_reset_config.spi_config.divisor = spi_divisor;
|
||||
driver->spi_dc_reset_config.spi_config.lsb_first = false;
|
||||
driver->spi_dc_reset_config.spi_config.mode = spi_mode;
|
||||
driver->spi_dc_reset_config.dc_pin = dc_pin;
|
||||
driver->spi_dc_reset_config.reset_pin = reset_pin;
|
||||
driver->base.comms_config = &driver->spi_dc_reset_config;
|
||||
driver->spi_dc_reset_config.spi_config.chip_select_pin = chip_select_pin;
|
||||
driver->spi_dc_reset_config.spi_config.divisor = spi_divisor;
|
||||
driver->spi_dc_reset_config.spi_config.lsb_first = false;
|
||||
driver->spi_dc_reset_config.spi_config.mode = spi_mode;
|
||||
driver->spi_dc_reset_config.dc_pin = dc_pin;
|
||||
driver->spi_dc_reset_config.reset_pin = reset_pin;
|
||||
driver->spi_dc_reset_config.command_params_uses_command_pin = false;
|
||||
|
||||
if (!qp_internal_register_device((painter_device_t)driver)) {
|
||||
memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright 2021 Paul Cotter (@gr1mr3aver)
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpio.h"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user