diff --git a/Makefile b/Makefile index c851e01e7314..c5c71c5a8f98 100644 --- a/Makefile +++ b/Makefile @@ -272,11 +272,24 @@ endef define PARSE_RULE RULE := $1 COMMANDS := + REQUIRE_PLATFORM_KEY := # If the rule starts with all, then continue the parsing from # PARSE_ALL_KEYBOARDS ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,all),true) KEYBOARD_RULE=all $$(eval $$(call PARSE_ALL_KEYBOARDS)) + else ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,all-avr),true) + KEYBOARD_RULE=all + REQUIRE_PLATFORM_KEY := avr + $$(eval $$(call PARSE_ALL_KEYBOARDS)) + else ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,all-chibios),true) + KEYBOARD_RULE=all + REQUIRE_PLATFORM_KEY := chibios + $$(eval $$(call PARSE_ALL_KEYBOARDS)) + else ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,all-arm_atsam),true) + KEYBOARD_RULE=all + REQUIRE_PLATFORM_KEY := arm_atsam + $$(eval $$(call PARSE_ALL_KEYBOARDS)) else ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,test),true) $$(eval $$(call PARSE_TEST)) # If the rule starts with the name of a known keyboard, then continue @@ -447,7 +460,7 @@ define PARSE_KEYMAP # Format it in bold KB_SP := $(BOLD)$$(KB_SP)$(NO_COLOR) # Specify the variables that we are passing forward to submake - MAKE_VARS := KEYBOARD=$$(CURRENT_KB) KEYMAP=$$(CURRENT_KM) + MAKE_VARS := KEYBOARD=$$(CURRENT_KB) KEYMAP=$$(CURRENT_KM) REQUIRE_PLATFORM_KEY=$$(REQUIRE_PLATFORM_KEY) # And the first part of the make command MAKE_CMD := $$(MAKE) -r -R -C $(ROOT_DIR) -f build_keyboard.mk $$(MAKE_TARGET) # The message to display @@ -466,6 +479,8 @@ define BUILD LOG=$$$$($$(MAKE_CMD) $$(MAKE_VARS) SILENT=true 2>&1) ; \ if [ $$$$? -gt 0 ]; \ then $$(PRINT_ERROR_PLAIN); \ + elif [ "$$$$LOG" = "skipped" ] ; \ + then $$(PRINT_SKIPPED_PLAIN); \ elif [ "$$$$LOG" != "" ] ; \ then $$(PRINT_WARNING_PLAIN); \ else \ @@ -637,12 +652,13 @@ else endif ifndef SKIP_VERSION BUILD_DATE := $(shell date +"%Y-%m-%d-%H:%M:%S") +else +BUILD_DATE := 2020-01-01-00:00:00 +endif + $(shell echo '#define QMK_VERSION "$(GIT_VERSION)"' > $(ROOT_DIR)/quantum/version.h) $(shell echo '#define QMK_BUILDDATE "$(BUILD_DATE)"' >> $(ROOT_DIR)/quantum/version.h) $(shell echo '#define CHIBIOS_VERSION "$(CHIBIOS_VERSION)"' >> $(ROOT_DIR)/quantum/version.h) $(shell echo '#define CHIBIOS_CONTRIB_VERSION "$(CHIBIOS_CONTRIB_VERSION)"' >> $(ROOT_DIR)/quantum/version.h) -else -BUILD_DATE := NA -endif include $(ROOT_DIR)/testlist.mk diff --git a/build_keyboard.mk b/build_keyboard.mk index b4e1efd9ee40..e87dcb8f22c8 100644 --- a/build_keyboard.mk +++ b/build_keyboard.mk @@ -317,6 +317,13 @@ SRC += $(TMK_COMMON_SRC) OPT_DEFS += $(TMK_COMMON_DEFS) EXTRALDFLAGS += $(TMK_COMMON_LDFLAGS) +SKIP_COMPILE := no +ifneq ($(REQUIRE_PLATFORM_KEY),) + ifneq ($(REQUIRE_PLATFORM_KEY),$(PLATFORM_KEY)) + SKIP_COMPILE := yes + endif +endif + include $(TMK_PATH)/$(PLATFORM_KEY).mk ifneq ($(strip $(PROTOCOL)),) include $(TMK_PATH)/protocol/$(strip $(shell echo $(PROTOCOL) | tr '[:upper:]' '[:lower:]')).mk @@ -352,7 +359,13 @@ $(KEYBOARD_OUTPUT)_INC := $(PROJECT_INC) $(GFXINC) $(KEYBOARD_OUTPUT)_CONFIG := $(PROJECT_CONFIG) # Default target. +ifeq ($(SKIP_COMPILE),no) all: build check-size +else +all: + echo "skipped" >&2 +endif + build: elf cpfirmware check-size: build objs-size: build diff --git a/build_test.mk b/build_test.mk index d13d9a515b84..e705c6a3ba1b 100644 --- a/build_test.mk +++ b/build_test.mk @@ -49,6 +49,7 @@ endif include common_features.mk include $(TMK_PATH)/common.mk +include $(QUANTUM_PATH)/sequencer/tests/rules.mk include $(QUANTUM_PATH)/serial_link/tests/rules.mk ifneq ($(filter $(FULL_TESTS),$(TEST)),) include build_full_test.mk diff --git a/common.mk b/common.mk index aea29a7a20e2..c13b5e2768a7 100644 --- a/common.mk +++ b/common.mk @@ -21,4 +21,5 @@ COMMON_VPATH += $(QUANTUM_PATH)/keymap_extras COMMON_VPATH += $(QUANTUM_PATH)/audio COMMON_VPATH += $(QUANTUM_PATH)/process_keycode COMMON_VPATH += $(QUANTUM_PATH)/api +COMMON_VPATH += $(QUANTUM_PATH)/sequencer COMMON_VPATH += $(DRIVER_PATH) diff --git a/common_features.mk b/common_features.mk index ba66c5324839..8ac53ec45a5f 100644 --- a/common_features.mk +++ b/common_features.mk @@ -21,6 +21,11 @@ QUANTUM_SRC += \ $(QUANTUM_DIR)/keymap_common.c \ $(QUANTUM_DIR)/keycode_config.c +ifeq ($(strip $(DEBUG_MATRIX_SCAN_RATE_ENABLE)), yes) + OPT_DEFS += -DDEBUG_MATRIX_SCAN_RATE + CONSOLE_ENABLE = yes +endif + ifeq ($(strip $(API_SYSEX_ENABLE)), yes) OPT_DEFS += -DAPI_SYSEX_ENABLE OPT_DEFS += -DAPI_ENABLE @@ -39,6 +44,13 @@ ifeq ($(strip $(AUDIO_ENABLE)), yes) SRC += $(QUANTUM_DIR)/audio/luts.c endif +ifeq ($(strip $(SEQUENCER_ENABLE)), yes) + OPT_DEFS += -DSEQUENCER_ENABLE + MUSIC_ENABLE = yes + SRC += $(QUANTUM_DIR)/sequencer/sequencer.c + SRC += $(QUANTUM_DIR)/process_keycode/process_sequencer.c +endif + ifeq ($(strip $(MIDI_ENABLE)), yes) OPT_DEFS += -DMIDI_ENABLE MUSIC_ENABLE = yes @@ -156,12 +168,14 @@ ifeq ($(strip $(RGBLIGHT_ENABLE)), yes) endif endif -VALID_MATRIX_TYPES := yes IS31FL3731 IS31FL3733 IS31FL3737 IS31FL3741 WS2812 custom LED_MATRIX_ENABLE ?= no -ifneq ($(strip $(LED_MATRIX_ENABLE)), no) - ifeq ($(filter $(LED_MATRIX_ENABLE),$(VALID_MATRIX_TYPES)),) - $(error LED_MATRIX_ENABLE="$(LED_MATRIX_ENABLE)" is not a valid matrix type) +VALID_LED_MATRIX_TYPES := IS31FL3731 custom +# TODO: IS31FL3733 IS31FL3737 IS31FL3741 + +ifeq ($(strip $(LED_MATRIX_ENABLE)), yes) + ifeq ($(filter $(LED_MATRIX_DRIVER),$(VALID_LED_MATRIX_TYPES)),) + $(error LED_MATRIX_DRIVER="$(LED_MATRIX_DRIVER)" is not a valid matrix type) else BACKLIGHT_ENABLE = yes BACKLIGHT_DRIVER = custom @@ -169,21 +183,22 @@ ifneq ($(strip $(LED_MATRIX_ENABLE)), no) SRC += $(QUANTUM_DIR)/led_matrix.c SRC += $(QUANTUM_DIR)/led_matrix_drivers.c endif -endif -ifeq ($(strip $(LED_MATRIX_ENABLE)), IS31FL3731) - OPT_DEFS += -DIS31FL3731 - COMMON_VPATH += $(DRIVER_PATH)/issi - SRC += is31fl3731-simple.c - QUANTUM_LIB_SRC += i2c_master.c + ifeq ($(strip $(LED_MATRIX_DRIVER)), IS31FL3731) + OPT_DEFS += -DIS31FL3731 -DSTM32_I2C -DHAL_USE_I2C=TRUE + COMMON_VPATH += $(DRIVER_PATH)/issi + SRC += is31fl3731-simple.c + QUANTUM_LIB_SRC += i2c_master.c + endif endif RGB_MATRIX_ENABLE ?= no +VALID_RGB_MATRIX_TYPES := IS31FL3731 IS31FL3733 IS31FL3737 IS31FL3741 WS2812 custom -ifneq ($(strip $(RGB_MATRIX_ENABLE)), no) -ifeq ($(filter $(RGB_MATRIX_ENABLE),$(VALID_MATRIX_TYPES)),) - $(error RGB_MATRIX_ENABLE="$(RGB_MATRIX_ENABLE)" is not a valid matrix type) -endif +ifeq ($(strip $(RGB_MATRIX_ENABLE)), yes) + ifeq ($(filter $(RGB_MATRIX_DRIVER),$(VALID_RGB_MATRIX_TYPES)),) + $(error "$(RGB_MATRIX_DRIVER)" is not a valid matrix type) + endif OPT_DEFS += -DRGB_MATRIX_ENABLE ifneq (,$(filter $(MCU), atmega16u2 atmega32u2)) # ATmegaxxU2 does not have hardware MUL instruction - lib8tion must be told to use software multiplication routines @@ -194,51 +209,47 @@ endif SRC += $(QUANTUM_DIR)/rgb_matrix_drivers.c CIE1931_CURVE := yes RGB_KEYCODES_ENABLE := yes -endif - -ifeq ($(strip $(RGB_MATRIX_ENABLE)), yes) - RGB_MATRIX_ENABLE := IS31FL3731 -endif -ifeq ($(strip $(RGB_MATRIX_ENABLE)), IS31FL3731) - OPT_DEFS += -DIS31FL3731 -DSTM32_I2C -DHAL_USE_I2C=TRUE - COMMON_VPATH += $(DRIVER_PATH)/issi - SRC += is31fl3731.c - QUANTUM_LIB_SRC += i2c_master.c -endif + ifeq ($(strip $(RGB_MATRIX_DRIVER)), IS31FL3731) + OPT_DEFS += -DIS31FL3731 -DSTM32_I2C -DHAL_USE_I2C=TRUE + COMMON_VPATH += $(DRIVER_PATH)/issi + SRC += is31fl3731.c + QUANTUM_LIB_SRC += i2c_master.c + endif -ifeq ($(strip $(RGB_MATRIX_ENABLE)), IS31FL3733) - OPT_DEFS += -DIS31FL3733 -DSTM32_I2C -DHAL_USE_I2C=TRUE - COMMON_VPATH += $(DRIVER_PATH)/issi - SRC += is31fl3733.c - QUANTUM_LIB_SRC += i2c_master.c -endif + ifeq ($(strip $(RGB_MATRIX_DRIVER)), IS31FL3733) + OPT_DEFS += -DIS31FL3733 -DSTM32_I2C -DHAL_USE_I2C=TRUE + COMMON_VPATH += $(DRIVER_PATH)/issi + SRC += is31fl3733.c + QUANTUM_LIB_SRC += i2c_master.c + endif -ifeq ($(strip $(RGB_MATRIX_ENABLE)), IS31FL3737) - OPT_DEFS += -DIS31FL3737 -DSTM32_I2C -DHAL_USE_I2C=TRUE - COMMON_VPATH += $(DRIVER_PATH)/issi - SRC += is31fl3737.c - QUANTUM_LIB_SRC += i2c_master.c -endif + ifeq ($(strip $(RGB_MATRIX_DRIVER)), IS31FL3737) + OPT_DEFS += -DIS31FL3737 -DSTM32_I2C -DHAL_USE_I2C=TRUE + COMMON_VPATH += $(DRIVER_PATH)/issi + SRC += is31fl3737.c + QUANTUM_LIB_SRC += i2c_master.c + endif -ifeq ($(strip $(RGB_MATRIX_ENABLE)), IS31FL3741) - OPT_DEFS += -DIS31FL3741 -DSTM32_I2C -DHAL_USE_I2C=TRUE - COMMON_VPATH += $(DRIVER_PATH)/issi - SRC += is31fl3741.c - QUANTUM_LIB_SRC += i2c_master.c -endif + ifeq ($(strip $(RGB_MATRIX_DRIVER)), IS31FL3741) + OPT_DEFS += -DIS31FL3741 -DSTM32_I2C -DHAL_USE_I2C=TRUE + COMMON_VPATH += $(DRIVER_PATH)/issi + SRC += is31fl3741.c + QUANTUM_LIB_SRC += i2c_master.c + endif -ifeq ($(strip $(RGB_MATRIX_ENABLE)), WS2812) - OPT_DEFS += -DWS2812 - WS2812_DRIVER_REQUIRED := yes -endif + ifeq ($(strip $(RGB_MATRIX_DRIVER)), WS2812) + OPT_DEFS += -DWS2812 + WS2812_DRIVER_REQUIRED := yes + endif -ifeq ($(strip $(RGB_MATRIX_CUSTOM_KB)), yes) - OPT_DEFS += -DRGB_MATRIX_CUSTOM_KB -endif + ifeq ($(strip $(RGB_MATRIX_CUSTOM_KB)), yes) + OPT_DEFS += -DRGB_MATRIX_CUSTOM_KB + endif -ifeq ($(strip $(RGB_MATRIX_CUSTOM_USER)), yes) - OPT_DEFS += -DRGB_MATRIX_CUSTOM_USER + ifeq ($(strip $(RGB_MATRIX_CUSTOM_USER)), yes) + OPT_DEFS += -DRGB_MATRIX_CUSTOM_USER + endif endif ifeq ($(strip $(RGB_KEYCODES_ENABLE)), yes) @@ -444,11 +455,14 @@ ifeq ($(strip $(SPLIT_KEYBOARD)), yes) # Functions added via QUANTUM_LIB_SRC are only included in the final binary if they're called. # Unused functions are pruned away, which is why we can add multiple drivers here without bloat. ifeq ($(PLATFORM),AVR) - QUANTUM_LIB_SRC += i2c_master.c \ - i2c_slave.c + ifneq ($(NO_I2C),yes) + QUANTUM_LIB_SRC += i2c_master.c \ + i2c_slave.c + endif endif SERIAL_DRIVER ?= bitbang + OPT_DEFS += -DSERIAL_DRIVER_$(strip $(shell echo $(SERIAL_DRIVER) | tr '[:lower:]' '[:upper:]')) ifeq ($(strip $(SERIAL_DRIVER)), bitbang) QUANTUM_LIB_SRC += serial.c else diff --git a/docs/ChangeLog/20201128.md b/docs/ChangeLog/20201128.md new file mode 100644 index 000000000000..86abd384df74 --- /dev/null +++ b/docs/ChangeLog/20201128.md @@ -0,0 +1,150 @@ +# QMK Breaking Change - 2020 Nov 28 Changelog + +Four times a year QMK runs a process for merging Breaking Changes. A Breaking Change is any change which modifies how QMK behaves in a way that is incompatible or potentially dangerous. We limit these changes to 4 times per year so that users can have confidence that updating their QMK tree will not break their keymaps. + + +## Changes Requiring User Action :id=changes-requiring-user-action + +### Relocated Keyboards :id-relocated-keyboards + +#### Reduce Helix keyboard build variation ([#8669](https://github.com/qmk/qmk_firmware/pull/8669)) + +The build commands for the Helix keyboard are: + +``` +make : +``` + +For ``, specify the one in the rightmost column of the table below, such as `helix`,` helix/pico`. + +| before Oct 17 2019 | Oct 17 2019 | Mar 10 2020 | Nov 28 2020 | +| ---------------------|-------------------------|-------------------------| ------------------------| +| helix/rev1 | helix/rev1 | helix/rev1 | helix/rev1 | +| helix/pico | helix/pico | helix/pico | helix/pico | +| | helix/pico/back | helix/pico/back | helix/pico/back | +| | helix/pico/under | helix/pico/under | helix/pico/under | +| | | helix/pico/sc | -- | +| | | helix/pico/sc/back | helix/pico/sc | +| | | helix/pico/sc/under | -- | +| helix/rev2 (=helix) | helix/rev2 (=helix) | helix/rev2 (=helix) | -- | +| | helix/rev2/back | helix/rev2/back | -- | +| | helix/rev2/back/oled | helix/rev2/back/oled | ( --> helix/rev2/back) | +| | helix/rev2/oled | helix/rev2/oled | helix/rev2 (=helix) | +| | helix/rev2/oled/back | helix/rev2/oled/back | helix/rev2/back | +| | helix/rev2/oled/under | helix/rev2/oled/under | helix/rev2/under | +| | | helix/rev2/sc | -- | +| | | helix/rev2/sc/back | -- | +| | | helix/rev2/sc/oled | -- | +| | | helix/rev2/sc/oledback | helix/rev2/sc | +| | | helix/rev2/sc/oledunder | -- | +| | | helix/rev2/sc/under | -- | +| | helix/rev2/under | helix/rev2/under | -- | +| | helix/rev2/under/oled | helix/rev2/under/oled | ( --> helix/rev2/under) | + +#### Update the Speedo firmware for v3.0 ([#10657](https://github.com/qmk/qmk_firmware/pull/10657)) + +The Speedo keyboard has moved to `cozykeys/speedo/v2` as the designer prepares to release the Speedo v3.0. + +| Previous Name | New Name | +| :------------ | :------------------------- | +| speedo | cozykeys/speedo/v2 | +| -- | cozykeys/speedo/v3 **new** | + +#### Maartenwut/Maarten name change to evyd13/Evy ([#10274](https://github.com/qmk/qmk_firmware/pull/10274)) + +Maartenwut has rebranded as @evyd13, and all released Maartenwut boards have moved. + +| Previous Name | New Name | +| :--------------------- | :----------------- | +| maartenwut/atom47/rev2 | evyd13/atom47/rev2 | +| maartenwut/atom47/rev3 | evyd13/atom47/rev3 | +| maartenwut/eon40 | evyd13/eon40 | +| maartenwut/eon65 | evyd13/eon65 | +| maartenwut/eon75 | evyd13/eon75 | +| maartenwut/eon87 | evyd13/eon87 | +| maartenwut/eon95 | evyd13/eon95 | +| maartenwut/gh80_1800 | evyd13/gh80_1800 | +| maartenwut/gh80_3700 | evyd13/gh80_3700 | +| maartenwut/minitomic | evyd13/minitomic | +| maartenwut/mx5160 | evyd13/mx5160 | +| maartenwut/nt660 | evyd13/nt660 | +| maartenwut/omrontkl | evyd13/omrontkl | +| maartenwut/plain60 | evyd13/plain60 | +| maartenwut/pockettype | evyd13/pockettype | +| maartenwut/quackfire | evyd13/quackfire | +| maartenwut/solheim68 | evyd13/solheim68 | +| maartenwut/ta65 | evyd13/ta65 | +| maartenwut/wasdat | evyd13/wasdat | +| maartenwut/wasdat_code | evyd13/wasdat_code | +| maartenwut/wonderland | evyd13/wonderland | + +#### Xelus Valor and Dawn60 Refactors ([#10512](https://github.com/qmk/qmk_firmware/pull/10512), [#10584](https://github.com/qmk/qmk_firmware/pull/10584)) + +The Valor and Dawn60 keyboards by Xelus22 both now require their revisions to be specified when compiling. + +| Previous Name | New Name | +| :------------ | :---------------- | +| xelus/dawn60 | xelus/dawn60/rev1 | +| xelus/valor | xelus/valor/rev1 | + + +### Updated Keyboard Codebases :id=keyboard-updates + +#### AEboards EXT65 Refactor ([#10820](https://github.com/qmk/qmk_firmware/pull/10820)) + +The EXT65 codebase has been reworked so keymaps can be used with either revision. + + +## Core Changes :id=core-changes + +### Fixes :id=core-fixes + +* Reconnect the USB if users wake up a computer from the keyboard to restore the USB state ([#10088](https://github.com/qmk/qmk_firmware/pull/10088)) +* Fix cursor position bug in oled_write_raw functions ([#10800](https://github.com/qmk/qmk_firmware/pull/10800)) + +### Additions and Enhancements :id=core-additions + +* Allow MATRIX_ROWS to be greater than 32 ([#10183](https://github.com/qmk/qmk_firmware/pull/10183)) +* Add support for soft serial to ATmega32U2 ([#10204](https://github.com/qmk/qmk_firmware/pull/10204)) +* Allow direct control of MIDI velocity value ([#9940](https://github.com/qmk/qmk_firmware/pull/9940)) +* Joystick 16-bit support ([#10439](https://github.com/qmk/qmk_firmware/pull/10439)) +* Allow encoder resolutions to be set per encoder ([#10259](https://github.com/qmk/qmk_firmware/pull/10259)) +* Share button state from mousekey to pointing_device ([#10179](https://github.com/qmk/qmk_firmware/pull/10179)) +* Add advanced/efficient RGB Matrix Indicators ([#8564](https://github.com/qmk/qmk_firmware/pull/8564)) +* OLED display update interval support ([#10388](https://github.com/qmk/qmk_firmware/pull/10388)) +* Per-Key Retro Tapping ([#10622](https://github.com/qmk/qmk_firmware/pull/10622)) +* Allow backlight duty cycle limit ([#10260](https://github.com/qmk/qmk_firmware/pull/10260)) +* Add step sequencer feature ([#9703](https://github.com/qmk/qmk_firmware/pull/9703)) +* Added `add_oneshot_mods` & `del_oneshot_mods` ([#10549](https://github.com/qmk/qmk_firmware/pull/10549)) +* Add AT90USB support for serial.c ([#10706](https://github.com/qmk/qmk_firmware/pull/10706)) +* Auto shift: support repeats and early registration (#9826) + +### Clean-ups and Optimizations :id=core-optimizations + +* Haptic and solenoid cleanup ([#9700](https://github.com/qmk/qmk_firmware/pull/9700)) +* XD75 cleanup ([#10524](https://github.com/qmk/qmk_firmware/pull/10524)) +* Minor change to behavior allowing display updates to continue between task ticks ([#10750](https://github.com/qmk/qmk_firmware/pull/10750)) +* Change some GPIO manipulations in matrix.c to be atomic ([#10491](https://github.com/qmk/qmk_firmware/pull/10491)) +* combine repeated lines of code for ATmega32U2, ATmega16U2, ATmega328 and ATmega328P ([#10837](https://github.com/qmk/qmk_firmware/pull/10837)) +* Remove references to HD44780 ([#10735](https://github.com/qmk/qmk_firmware/pull/10735)) + + +## QMK Infrastructure and Internals :id=qmk-internals + +* Add ability to build a subset of all keyboards based on platform. ([#10420](https://github.com/qmk/qmk_firmware/pull/10420)) +* Initialise EEPROM drivers at startup, instead of upon first execution ([#10438](https://github.com/qmk/qmk_firmware/pull/10438)) +* Make bootloader_jump weak for ChibiOS ([#10417](https://github.com/qmk/qmk_firmware/pull/10417)) +* Support for STM32 GPIOF,G,H,I,J,K ([#10206](https://github.com/qmk/qmk_firmware/pull/10206)) +* Add milc as a dependency and remove the installed milc ([#10563](https://github.com/qmk/qmk_firmware/pull/10563)) +* ChibiOS upgrade: early init conversions ([#10214](https://github.com/qmk/qmk_firmware/pull/10214)) +* ChibiOS upgrade: configuration file migrator ([#9952](https://github.com/qmk/qmk_firmware/pull/9952)) +* Add definition based on currently-selected serial driver. ([#10716](https://github.com/qmk/qmk_firmware/pull/10716)) +* Allow for modification of output RGB values when using rgblight/rgb_matrix. ([#10638](https://github.com/qmk/qmk_firmware/pull/10638)) +* Allow keyboards/keymaps to execute code at each main loop iteration ([#10530](https://github.com/qmk/qmk_firmware/pull/10530)) +* qmk cformat ([#10767](https://github.com/qmk/qmk_firmware/pull/10767)) +* Add a Make variable to easily enable DEBUG_MATRIX_SCAN_RATE on the command line ([#10824](https://github.com/qmk/qmk_firmware/pull/10824)) +* update Chibios OS USB for the OTG driver ([#8893](https://github.com/qmk/qmk_firmware/pull/8893)) +* Fixup version.h writing when using `SKIP_VERSION=yes` ([#10972](https://github.com/qmk/qmk_firmware/pull/10972), [#10974](https://github.com/qmk/qmk_firmware/pull/10974)) +* Rename ledmatrix.h to match .c file ([#7949](https://github.com/qmk/qmk_firmware/pull/7949)) +* Split RGB_MATRIX_ENABLE into _ENABLE and _DRIVER ([#10231](https://github.com/qmk/qmk_firmware/pull/10231)) +* Split LED_MATRIX_ENABLE into _ENABLE and _DRIVER ([#10840](https://github.com/qmk/qmk_firmware/pull/10840)) diff --git a/docs/_summary.md b/docs/_summary.md index 44030d812d47..19498f6a205b 100644 --- a/docs/_summary.md +++ b/docs/_summary.md @@ -80,6 +80,7 @@ * [One Shot Keys](one_shot_keys.md) * [Pointing Device](feature_pointing_device.md) * [Raw HID](feature_rawhid.md) + * [Sequencer](feature_sequencer.md) * [Swap Hands](feature_swap_hands.md) * [Tap Dance](feature_tap_dance.md) * [Tap-Hold Configuration](tap_hold.md) @@ -119,6 +120,7 @@ * [Overview](breaking_changes.md) * [My Pull Request Was Flagged](breaking_changes_instructions.md) * History + * [2020 Nov 28](ChangeLog/20201128.md) * [2020 Aug 29](ChangeLog/20200829.md) * [2020 May 30](ChangeLog/20200530.md) * [2020 Feb 29](ChangeLog/20200229.md) diff --git a/docs/breaking_changes.md b/docs/breaking_changes.md index abace81648c6..3ee14f2bfcfb 100644 --- a/docs/breaking_changes.md +++ b/docs/breaking_changes.md @@ -6,6 +6,7 @@ The breaking change period is when we will merge PR's that change QMK in dangero ## What has been included in past Breaking Changes? +* [2020 Nov 28](ChangeLog/20201128.md) * [2020 Aug 29](ChangeLog/20200829.md) * [2020 May 30](ChangeLog/20200530.md) * [2020 Feb 29](ChangeLog/20200229.md) @@ -13,16 +14,16 @@ The breaking change period is when we will merge PR's that change QMK in dangero ## When is the next Breaking Change? -The next Breaking Change is scheduled for November 28, 2020. +The next Breaking Change is scheduled for February 27, 2021. ### Important Dates -* [x] 2020 Aug 29 - `develop` is created. It will be rebased weekly. -* [ ] 2020 Oct 31 - `develop` closed to new PR's. -* [ ] 2020 Oct 31 - Call for testers. -* [ ] 2020 Nov 26 - `master` is locked, no PR's merged. -* [ ] 2020 Nov 28 - Merge `develop` to `master`. -* [ ] 2020 Nov 28 - `master` is unlocked. PR's can be merged again. +* [x] 2020 Nov 28 - `develop` is created. Each push to `master` is subsequently merged to `develop` +* [ ] 2021 Jan 30 - `develop` closed to new PR's. +* [ ] 2021 Jan 30 - Call for testers. +* [ ] 2021 Feb 25 - `master` is locked, no PR's merged. +* [ ] 2021 Feb 27 - Merge `develop` to `master`. +* [ ] 2021 Feb 27 - `master` is unlocked. PR's can be merged again. ## What changes will be included? @@ -39,21 +40,6 @@ Criteria for acceptance: This section documents various processes we use when running the Breaking Changes process. -## Rebase `develop` from `master` - -This is run every Friday while `develop` is open. - -Process: - -``` -cd qmk_firmware -git checkout master -git pull --ff-only -git checkout develop -git rebase master -git push --force -``` - ## Creating the `develop` branch This happens immediately after the previous `develop` branch is merged. diff --git a/docs/config_options.md b/docs/config_options.md index f9b1cc657870..3a4d7c87c52b 100644 --- a/docs/config_options.md +++ b/docs/config_options.md @@ -135,6 +135,8 @@ If you define these options you will enable the associated feature, which may in * `#define RETRO_TAPPING` * tap anyway, even after TAPPING_TERM, if there was no other key interruption between press and release * See [Retro Tapping](tap_hold.md#retro-tapping) for details +* `#define RETRO_TAPPING_PER_KEY` + * enables handling for per key `RETRO_TAPPING` settings * `#define TAPPING_TOGGLE 2` * how many taps before triggering the toggle * `#define PERMISSIVE_HOLD` diff --git a/docs/custom_quantum_functions.md b/docs/custom_quantum_functions.md index a459042b32e2..694b421e79ce 100644 --- a/docs/custom_quantum_functions.md +++ b/docs/custom_quantum_functions.md @@ -185,6 +185,14 @@ This function gets called at every matrix scan, which is basically as often as t You should use this function if you need custom matrix scanning code. It can also be used for custom status output (such as LEDs or a display) or other functionality that you want to trigger regularly even when the user isn't typing. +# Keyboard housekeeping + +* Keyboard/Revision: `void housekeeping_task_kb(void)` +* Keymap: `void housekeeping_task_user(void)` + +This function gets called at the end of all QMK processing, before starting the next iteration. You can safely assume that QMK has dealt with the last matrix scan at the time that these functions are invoked -- layer states have been updated, USB reports have been sent, LEDs have been updated, and displays have been drawn. + +Similar to `matrix_scan_*`, these are called as often as the MCU can handle. To keep your board responsive, it's suggested to do as little as possible during these function calls, potentially throtting their behaviour if you do indeed require implementing something special. # Keyboard Idling/Wake Code diff --git a/docs/feature_auto_shift.md b/docs/feature_auto_shift.md index b21a7690d9e4..8e04d9dd38cf 100644 --- a/docs/feature_auto_shift.md +++ b/docs/feature_auto_shift.md @@ -15,25 +15,31 @@ problem. When you tap a key, it stays depressed for a short period of time before it is then released. This depressed time is a different length for everyone. Auto Shift defines a constant `AUTO_SHIFT_TIMEOUT` which is typically set to twice your -normal pressed state time. When you press a key, a timer starts and then stops -when you release the key. If the time depressed is greater than or equal to the -`AUTO_SHIFT_TIMEOUT`, then a shifted version of the key is emitted. If the time -is less than the `AUTO_SHIFT_TIMEOUT` time, then the normal state is emitted. +normal pressed state time. When you press a key, a timer starts, and if you +have not released the key after the `AUTO_SHIFT_TIMEOUT` period, then a shifted +version of the key is emitted. If the time is less than the `AUTO_SHIFT_TIMEOUT` +time, or you press another key, then the normal state is emitted. + +If `AUTO_SHIFT_REPEAT` is defined, there is keyrepeat support. Holding the key +down will repeat the shifted key, though this can be disabled with +`AUTO_SHIFT_NO_AUTO_REPEAT`. If you want to repeat the normal key, then tap it +once then immediately (within `TAPPING_TERM`) hold it down again (this works +with the shifted value as well if auto-repeat is disabled). ## Are There Limitations to Auto Shift? Yes, unfortunately. -1. Key repeat will cease to work. For example, before if you wanted 20 'a' - characters, you could press and hold the 'a' key for a second or two. This no - longer works with Auto Shift because it is timing your depressed time instead - of emitting a depressed key state to your operating system. -2. You will have characters that are shifted when you did not intend on shifting, and - other characters you wanted shifted, but were not. This simply comes down to - practice. As we get in a hurry, we think we have hit the key long enough - for a shifted version, but we did not. On the other hand, we may think we are - tapping the keys, but really we have held it for a little longer than - anticipated. +You will have characters that are shifted when you did not intend on shifting, and +other characters you wanted shifted, but were not. This simply comes down to +practice. As we get in a hurry, we think we have hit the key long enough for a +shifted version, but we did not. On the other hand, we may think we are tapping +the keys, but really we have held it for a little longer than anticipated. + +Additionally, with keyrepeat the desired shift state can get mixed up. It will +always 'belong' to the last key pressed. For example, keyrepeating a capital +and then tapping something lowercase (whether or not it's an Auto Shift key) +will result in the capital's *key* still being held, but shift not. ## How Do I Enable Auto Shift? @@ -103,6 +109,14 @@ Do not Auto Shift numeric keys, zero through nine. Do not Auto Shift alpha characters, which include A through Z. +### AUTO_SHIFT_REPEAT (simple define) + +Enables keyrepeat. + +### AUTO_SHIFT_NO_AUTO_REPEAT (simple define) + +Disables automatically keyrepeating when `AUTO_SHIFT_TIMEOUT` is exceeded. + ## Using Auto Shift Setup This will enable you to define three keys temporarily to increase, decrease and report your `AUTO_SHIFT_TIMEOUT`. diff --git a/docs/feature_backlight.md b/docs/feature_backlight.md index 6bb2bbed844b..a558af64e144 100644 --- a/docs/feature_backlight.md +++ b/docs/feature_backlight.md @@ -62,14 +62,15 @@ Valid driver values are `pwm`, `software`, `custom` or `no`. See below for help To configure the backlighting, `#define` these in your `config.h`: -|Define |Default |Description | -|---------------------|-------------|-------------------------------------------------------------------------------------| -|`BACKLIGHT_PIN` |*Not defined*|The pin that controls the LED(s) | -|`BACKLIGHT_LEVELS` |`3` |The number of brightness levels (maximum 31 excluding off) | -|`BACKLIGHT_CAPS_LOCK`|*Not defined*|Enable Caps Lock indicator using backlight (for keyboards without dedicated LED) | -|`BACKLIGHT_BREATHING`|*Not defined*|Enable backlight breathing, if supported | -|`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| +| Define | Default | Description | +|------------------------|---------------|-------------------------------------------------------------------------------------------------------------------| +| `BACKLIGHT_PIN` | *Not defined* | The pin that controls the LED(s) | +| `BACKLIGHT_LEVELS` | `3` | The number of brightness levels (maximum 31 excluding off) | +| `BACKLIGHT_CAPS_LOCK` | *Not defined* | Enable Caps Lock indicator using backlight (for keyboards without dedicated LED) | +| `BACKLIGHT_BREATHING` | *Not defined* | Enable backlight breathing, if supported | +| `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. | Unless you are designing your own keyboard, you generally should not need to change the `BACKLIGHT_PIN` or `BACKLIGHT_ON_STATE`. diff --git a/docs/feature_encoders.md b/docs/feature_encoders.md index 8f9ba1a80a79..e2cafdac48e8 100644 --- a/docs/feature_encoders.md +++ b/docs/feature_encoders.md @@ -32,13 +32,20 @@ Additionally, the resolution, which defines how many pulses the encoder register #define ENCODER_RESOLUTION 4 ``` +It can also be defined per-encoder, by instead defining: + +```c +#define ENCODER_RESOLUTIONS { 4, 2 } +``` + ## Split Keyboards -If you are using different pinouts for the encoders on each half of a split keyboard, you can define the pinout for the right half like this: +If you are using different pinouts for the encoders on each half of a split keyboard, you can define the pinout (and optionally, resolutions) for the right half like this: ```c #define ENCODERS_PAD_A_RIGHT { encoder1a, encoder2a } #define ENCODERS_PAD_B_RIGHT { encoder1b, encoder2b } +#define ENCODER_RESOLUTIONS_RIGHT { 2, 4 } ``` ## Callbacks diff --git a/docs/feature_haptic_feedback.md b/docs/feature_haptic_feedback.md index ff7337a51a44..64b66310b6ca 100644 --- a/docs/feature_haptic_feedback.md +++ b/docs/feature_haptic_feedback.md @@ -42,14 +42,21 @@ First you will need a build a circuit to drive the solenoid through a mosfet as [Wiring diagram provided by Adafruit](https://playground.arduino.cc/uploads/Learning/solenoid_driver.pdf) -| Settings | Default | Description | -|--------------------------|---------------|-------------------------------------------------------| -|`SOLENOID_PIN` | *Not defined* |Configures the pin that the Solenoid is connected to. | -|`SOLENOID_DEFAULT_DWELL` | `12` ms |Configures the default dwell time for the solenoid. | -|`SOLENOID_MIN_DWELL` | `4` ms |Sets the lower limit for the dwell. | -|`SOLENOID_MAX_DWELL` | `100` ms |Sets the upper limit for the dwell. | - -?> Dwell time is how long the "plunger" stays activated. The dwell time changes how the solenoid sounds. +| Settings | Default | Description | +|----------------------------|----------------------|-------------------------------------------------------| +|`SOLENOID_PIN` | *Not defined* |Configures the pin that the Solenoid is connected to. | +|`SOLENOID_DEFAULT_DWELL` | `12` ms |Configures the default dwell time for the solenoid. | +|`SOLENOID_MIN_DWELL` | `4` ms |Sets the lower limit for the dwell. | +|`SOLENOID_MAX_DWELL` | `100` ms |Sets the upper limit for the dwell. | +|`SOLENOID_DWELL_STEP_SIZE` | `1` ms |The step size to use when `HPT_DWL*` keycodes are sent | +|`SOLENOID_DEFAULT_BUZZ` | `0` (disabled) |On HPT_RST buzz is set "on" if this is "1" | +|`SOLENOID_BUZZ_ACTUATED` | `SOLENOID_MIN_DWELL` |Actuated-time when the solenoid is in buzz mode | +|`SOLENOID_BUZZ_NONACTUATED` | `SOLENOID_MIN_DWELL` |Non-Actuated-time when the solenoid is in buzz mode | + +* If solenoid buzz is off, then dwell time is how long the "plunger" stays activated. The dwell time changes how the solenoid sounds. +* If solenoid buzz is on, then dwell time sets the length of the buzz, while `SOLENOID_BUZZ_ACTUATED` and `SOLENOID_BUZZ_NONACTUATED` set the (non-)actuation times withing the buzz period. +* With the current implementation, for any of the above time settings, the precision of these settings may be affected by how fast the keyboard is able to scan the matrix. + Therefore, if the keyboards scanning routine is slow, it may be preferable to set `SOLENOID_DWELL_STEP_SIZE` to a value slightly smaller than the time it takes to scan the keyboard. Beware that some pins may be powered during bootloader (ie. A13 on the STM32F303 chip) and will result in the solenoid kept in the on state through the whole flashing process. This may overheat and damage the solenoid. If you find that the pin the solenoid is connected to is triggering the solenoid during bootloader/DFU, select another pin. diff --git a/docs/feature_joystick.md b/docs/feature_joystick.md index be3c781f6cd4..12bbf5b35eff 100644 --- a/docs/feature_joystick.md +++ b/docs/feature_joystick.md @@ -141,6 +141,12 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) { } ``` +### Axis Resolution + +By default, the resolution of each axis is 8 bit, giving a range of -127 to +127. If you need higher precision, you can increase it by defining eg. `JOYSTICK_AXES_RESOLUTION 12` in your `config.h`. The resolution must be between 8 and 16. + +Note that the supported AVR MCUs have a 10-bit ADC, and 12-bit for most STM32 MCUs. + ### Triggering Joystick Buttons Joystick buttons are normal Quantum keycodes, defined as `JS_BUTTON0` to `JS_BUTTON31`, depending on the number of buttons you have configured. diff --git a/docs/feature_led_matrix.md b/docs/feature_led_matrix.md index 372407b90c2a..a4e9d7eb8369 100644 --- a/docs/feature_led_matrix.md +++ b/docs/feature_led_matrix.md @@ -10,7 +10,8 @@ If you want to use RGB LED's you should use the [RGB Matrix Subsystem](feature_r There is basic support for addressable LED matrix lighting with the I2C IS31FL3731 RGB controller. To enable it, add this to your `rules.mk`: - LED_MATRIX_ENABLE = IS31FL3731 + LED_MATRIX_ENABLE = yes + LED_MATRIX_DRIVER = IS31FL3731 You can use between 1 and 4 IS31FL3731 IC's. Do not specify `LED_DRIVER_ADDR_` defines for IC's that are not present on your keyboard. You can define the following items in `config.h`: diff --git a/docs/feature_mouse_keys.md b/docs/feature_mouse_keys.md index a6b46bc151f3..ffde13389242 100644 --- a/docs/feature_mouse_keys.md +++ b/docs/feature_mouse_keys.md @@ -140,3 +140,7 @@ To use constant speed mode, you must at least define `MK_COMBINED` in your keyma ```c #define MK_COMBINED ``` + +## Use with PS/2 Mouse and Pointing Device + +Mouse keys button state is shared with [PS/2 mouse](feature_ps2_mouse.md) and [pointing device](feature_pointing_device.md) so mouse keys button presses can be used for clicks and drags. diff --git a/docs/feature_oled_driver.md b/docs/feature_oled_driver.md index 9f0ef1664b10..44202487f15e 100644 --- a/docs/feature_oled_driver.md +++ b/docs/feature_oled_driver.md @@ -150,6 +150,7 @@ void oled_task_user(void) { |`OLED_IC` |`OLED_IC_SSD1306`|Set to `OLED_IC_SH1106` if you're using the SH1106 OLED controller. | |`OLED_COLUMN_OFFSET` |`0` |(SH1106 only.) Shift output to the right this many pixels.
Useful for 128x64 displays centered on a 132x64 SH1106 IC.| |`OLED_BRIGHTNESS` |`255` |The default brightness level of the OLED, from 0 to 255. | +|`OLED_UPDATE_INTERVAL` |`0` |Set the time interval for updating the OLED display in ms. This will improve the matrix scan rate. | ## 128x64 & Custom sized OLED Displays diff --git a/docs/feature_rgb_matrix.md b/docs/feature_rgb_matrix.md index b70a5fcba082..f8b0653551f4 100644 --- a/docs/feature_rgb_matrix.md +++ b/docs/feature_rgb_matrix.md @@ -11,7 +11,8 @@ If you want to use single color LED's you should use the [LED Matrix Subsystem]( There is basic support for addressable RGB matrix lighting with the I2C IS31FL3731 RGB controller. To enable it, add this to your `rules.mk`: ```makefile -RGB_MATRIX_ENABLE = IS31FL3731 +RGB_MATRIX_ENABLE = yes +RGB_MATRIX_DRIVER = IS31FL3731 ``` Configure the hardware via your `config.h`: @@ -62,7 +63,8 @@ Where `Cx_y` is the location of the LED in the matrix defined by [the datasheet] There is basic support for addressable RGB matrix lighting with the I2C IS31FL3733 RGB controller. To enable it, add this to your `rules.mk`: ```makefile -RGB_MATRIX_ENABLE = IS31FL3733 +RGB_MATRIX_ENABLE = yes +RGB_MATRIX_DRIVER = IS31FL3733 ``` Configure the hardware via your `config.h`: @@ -112,7 +114,8 @@ Where `X_Y` is the location of the LED in the matrix defined by [the datasheet]( There is basic support for addressable RGB matrix lighting with a WS2811/WS2812{a,b,c} addressable LED strand. To enable it, add this to your `rules.mk`: ```makefile -RGB_MATRIX_ENABLE = WS2812 +RGB_MATRIX_ENABLE = yes +RGB_MATRIX_DRIVER = WS2812 ``` Configure the hardware via your `config.h`: @@ -482,6 +485,14 @@ void rgb_matrix_indicators_kb(void) { } ``` +In addition, there are the advanced indicator functions. These are aimed at those with heavily customized displays, where rendering every LED per cycle is expensive. Such as some of the "drashna" layouts. This includes a special macro to help make this easier to use: `RGB_MATRIX_INDICATOR_SET_COLOR(i, r, g, b)`. + +```c +void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) { + RGB_MATRIX_INDICATOR_SET_COLOR(index, red, green, blue); +} +``` + ### Suspended state :id=suspended-state To use the suspend feature, make sure that `#define RGB_DISABLE_WHEN_USB_SUSPENDED true` is added to the `config.h` file. diff --git a/docs/feature_sequencer.md b/docs/feature_sequencer.md new file mode 100644 index 000000000000..8c8587a9b846 --- /dev/null +++ b/docs/feature_sequencer.md @@ -0,0 +1,88 @@ +# Sequencer + +Since QMK has experimental support for MIDI, you can now turn your keyboard into a [step sequencer](https://en.wikipedia.org/wiki/Music_sequencer#Step_sequencers)! + +!> **IMPORTANT:** This feature is highly experimental, it has only been tested on a Planck EZ so far. Also, the scope will be limited to support the drum machine use-case to start with. + +## Enable the step sequencer + +Add the following line to your `rules.mk`: + +```make +SEQUENCER_ENABLE = yes +``` + +By default the sequencer has 16 steps, but you can override this setting in your `config.h`: + +```c +#define SEQUENCER_STEPS 32 +``` + +## Tracks + +You can program up to 8 independent tracks with the step sequencer. Select the tracks you want to edit, enable or disable some steps, and start the sequence! + +## Resolutions + +While the tempo defines the absolute speed at which the sequencer goes through the steps, the resolution defines the granularity of these steps (from coarser to finer). + +|Resolution |Description | +|---------- |----------- | +|`SQ_RES_2` |Every other beat | +|`SQ_RES_2T` |Every 1.5 beats | +|`SQ_RES_4` |Every beat | +|`SQ_RES_4T` |Three times per 2 beats| +|`SQ_RES_8` |Twice per beat | +|`SQ_RES_8T` |Three times per beat | +|`SQ_RES_16` |Four times per beat | +|`SQ_RES_16T` |Six times per beat | +|`SQ_RES_32` |Eight times per beat | + +## Keycodes + +|Keycode |Description | +|------- |----------- | +|`SQ_ON` |Start the step sequencer | +|`SQ_OFF` |Stop the step sequencer | +|`SQ_TOG` |Toggle the step sequencer playback | +|`SQ_SALL`|Enable all the steps | +|`SQ_SCLR`|Disable all the steps | +|`SQ_S(n)`|Toggle the step `n` | +|`SQ_TMPD`|Decrease the tempo | +|`SQ_TMPU`|Increase the tempo | +|`SQ_R(n)`|Set the resolution to n | +|`SQ_RESD`|Change to the slower resolution | +|`SQ_RESU`|Change to the faster resolution | +|`SQ_T(n)`|Set `n` as the only active track or deactivate all | + +## Functions + +|Function |Description | +|-------- |----------- | +|`bool is_sequencer_on(void);` |Return whether the sequencer is playing | +|`void sequencer_toggle(void);` |Toggle the step sequencer playback | +|`void sequencer_on(void);` |Start the step sequencer | +|`void sequencer_off(void);` |Stop the step sequencer | +|`bool is_sequencer_step_on(uint8_t step);` |Return whether the step is currently enabled | +|`void sequencer_set_step(uint8_t step, bool value);` |Enable or disable the step | +|`void sequencer_set_step_on();` |Enable the step | +|`void sequencer_set_step_off();` |Disable the step | +|`void sequencer_toggle_step(uint8_t step);` |Toggle the step | +|`void sequencer_set_all_steps(bool value);` |Enable or disable all the steps | +|`void sequencer_set_all_steps_on();` |Enable all the steps | +|`void sequencer_set_all_steps_off();` |Disable all the steps | +|`uint8_t sequencer_get_tempo(void);` |Return the current tempo | +|`void sequencer_set_tempo(uint8_t tempo);` |Set the tempo to `tempo` (between 1 and 255) | +|`void sequencer_increase_tempo(void);` |Increase the tempo | +|`void sequencer_decrease_tempo(void);` |Decrease the tempo | +|`sequencer_resolution_t sequencer_get_resolution(void);` |Return the current resolution | +|`void sequencer_set_resolution(sequencer_resolution_t resolution);` |Set the resolution to `resolution` | +|`void sequencer_increase_resolution(void);` |Change to the faster resolution | +|`void sequencer_decrease_resolution(void);` |Change to the slower resolution | +|`bool is_sequencer_track_active(uint8_t track);` |Return whether the track is active | +|`void sequencer_set_track_activation(uint8_t track, bool value);` |Activate or deactivate the `track` | +|`void sequencer_toggle_track_activation(uint8_t track);` |Toggle the `track` | +|`void sequencer_activate_track(uint8_t track);` |Activate the `track` | +|`void sequencer_deactivate_track(uint8_t track);` |Deactivate the `track` | +|`void sequencer_toggle_single_active_track(uint8_t track);` |Set `track` as the only active track or deactivate all | + diff --git a/docs/internals_gpio_control.md b/docs/internals_gpio_control.md index 48eaf8875b71..7c2228949b0b 100644 --- a/docs/internals_gpio_control.md +++ b/docs/internals_gpio_control.md @@ -21,3 +21,22 @@ The following functions can provide basic control of GPIOs and are found in `qua ## Advanced Settings :id=advanced-settings Each microcontroller can have multiple advanced settings regarding its GPIO. This abstraction layer does not limit the use of architecture-specific functions. Advanced users should consult the datasheet of their desired device and include any needed libraries. For AVR, the standard avr/io.h library is used; for STM32, the ChibiOS [PAL library](http://chibios.sourceforge.net/docs3/hal/group___p_a_l.html) is used. + +## Atomic Operation + +The above functions are not always guaranteed to work atomically. Therefore, if you want to prevent interruptions in the middle of operations when using multiple combinations of the above functions, use the following `ATOMIC_BLOCK_FORCEON` macro. + +eg. +```c +void some_function() { + // some process + ATOMIC_BLOCK_FORCEON { + // Atomic Processing + } + // some process +} +``` + +`ATOMIC_BLOCK_FORCEON` forces interrupts to be disabled before the block is executed, without regard to whether they are enabled or disabled. Then, after the block is executed, the interrupt is enabled. + +Note that `ATOMIC_BLOCK_FORCEON` can therefore be used if you know that interrupts are enabled before the execution of the block, or if you know that it is OK to enable interrupts at the completion of the block. diff --git a/docs/ja/feature_led_matrix.md b/docs/ja/feature_led_matrix.md index b2595f9989e8..e7b60f594b45 100644 --- a/docs/ja/feature_led_matrix.md +++ b/docs/ja/feature_led_matrix.md @@ -15,7 +15,8 @@ RGB LED を使いたい場合は、代わりに [RGB マトリックスサブシ I2C IS31FL3731 RGB コントローラを使ったアドレス指定可能な LED マトリックスライトのための基本的なサポートがあります:有効にするには、`rules.mk` に以下を追加します: - LED_MATRIX_ENABLE = IS31FL3731 + LED_MATRIX_ENABLE = yes + LED_MATRIX_DRIVER = IS31FL3731 1から4個の IS31FL3731 IC を使うことができます。キーボード上に存在しない IC の `LED_DRIVER_ADDR_` 定義を指定しないでください。`config.h` に以下の項目を定義することができます: diff --git a/docs/tap_hold.md b/docs/tap_hold.md index 9ffbfde8fc19..aacff4004201 100644 --- a/docs/tap_hold.md +++ b/docs/tap_hold.md @@ -179,6 +179,25 @@ Holding and releasing a dual function key without pressing another key will resu For instance, holding and releasing `LT(2, KC_SPACE)` without hitting another key will result in nothing happening. With this enabled, it will send `KC_SPACE` instead. +For more granular control of this feature, you can add the following to your `config.h`: + +```c +#define RETRO_TAPPING_PER_KEY +``` + +You can then add the following function to your keymap: + +```c +bool get_retro_tapping(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case LT(2, KC_SPACE): + return true; + default: + return false; + } +} +``` + ## Why do we include the key record for the per key functions? One thing that you may notice is that we include the key record for all of the "per key" functions, and may be wondering why we do that. diff --git a/drivers/avr/serial.c b/drivers/avr/serial.c index c27cbfdd0a79..526a0946b268 100644 --- a/drivers/avr/serial.c +++ b/drivers/avr/serial.c @@ -20,50 +20,111 @@ #ifdef SOFT_SERIAL_PIN -# ifdef __AVR_ATmega32U4__ -// if using ATmega32U4 I2C, can not use PD0 and PD1 in soft serial. -# ifdef USE_AVR_I2C -# if SOFT_SERIAL_PIN == D0 || SOFT_SERIAL_PIN == D1 -# error Using ATmega32U4 I2C, so can not use PD0, PD1 -# endif +# if !(defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)) +# error serial.c is not supported for the currently selected MCU +# endif +// if using ATmega32U4/2, AT90USBxxx I2C, can not use PD0 and PD1 in soft serial. +# if defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) +# if defined(USE_AVR_I2C) && (SOFT_SERIAL_PIN == D0 || SOFT_SERIAL_PIN == D1) +# error Using I2C, so can not use PD0, PD1 # endif +# endif +// PD0..PD3, common config +# if SOFT_SERIAL_PIN == D0 +# define EIMSK_BIT _BV(INT0) +# define EICRx_BIT (~(_BV(ISC00) | _BV(ISC01))) +# define SERIAL_PIN_INTERRUPT INT0_vect +# define EICRx EICRA +# elif SOFT_SERIAL_PIN == D1 +# define EIMSK_BIT _BV(INT1) +# define EICRx_BIT (~(_BV(ISC10) | _BV(ISC11))) +# define SERIAL_PIN_INTERRUPT INT1_vect +# define EICRx EICRA +# elif SOFT_SERIAL_PIN == D2 +# define EIMSK_BIT _BV(INT2) +# define EICRx_BIT (~(_BV(ISC20) | _BV(ISC21))) +# define SERIAL_PIN_INTERRUPT INT2_vect +# define EICRx EICRA +# elif SOFT_SERIAL_PIN == D3 +# define EIMSK_BIT _BV(INT3) +# define EICRx_BIT (~(_BV(ISC30) | _BV(ISC31))) +# define SERIAL_PIN_INTERRUPT INT3_vect +# define EICRx EICRA +# endif -# define setPinInputHigh(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) |= _BV((pin)&0xF)) -# define setPinOutput(pin) (DDRx_ADDRESS(pin) |= _BV((pin)&0xF)) -# define writePinHigh(pin) (PORTx_ADDRESS(pin) |= _BV((pin)&0xF)) -# define writePinLow(pin) (PORTx_ADDRESS(pin) &= ~_BV((pin)&0xF)) -# define readPin(pin) ((bool)(PINx_ADDRESS(pin) & _BV((pin)&0xF))) - -# if SOFT_SERIAL_PIN >= D0 && SOFT_SERIAL_PIN <= D3 -# if SOFT_SERIAL_PIN == D0 -# define EIMSK_BIT _BV(INT0) -# define EICRx_BIT (~(_BV(ISC00) | _BV(ISC01))) -# define SERIAL_PIN_INTERRUPT INT0_vect -# elif SOFT_SERIAL_PIN == D1 -# define EIMSK_BIT _BV(INT1) -# define EICRx_BIT (~(_BV(ISC10) | _BV(ISC11))) -# define SERIAL_PIN_INTERRUPT INT1_vect -# elif SOFT_SERIAL_PIN == D2 -# define EIMSK_BIT _BV(INT2) -# define EICRx_BIT (~(_BV(ISC20) | _BV(ISC21))) -# define SERIAL_PIN_INTERRUPT INT2_vect -# elif SOFT_SERIAL_PIN == D3 -# define EIMSK_BIT _BV(INT3) -# define EICRx_BIT (~(_BV(ISC30) | _BV(ISC31))) -# define SERIAL_PIN_INTERRUPT INT3_vect -# endif +// ATmegaxxU2 specific config +# if defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) +// PD4(INT5), PD6(INT6), PD7(INT7), PC7(INT4) +# if SOFT_SERIAL_PIN == D4 +# define EIMSK_BIT _BV(INT5) +# define EICRx_BIT (~(_BV(ISC50) | _BV(ISC51))) +# define SERIAL_PIN_INTERRUPT INT5_vect +# define EICRx EICRB +# elif SOFT_SERIAL_PIN == D6 +# define EIMSK_BIT _BV(INT6) +# define EICRx_BIT (~(_BV(ISC60) | _BV(ISC61))) +# define SERIAL_PIN_INTERRUPT INT6_vect +# define EICRx EICRB +# elif SOFT_SERIAL_PIN == D7 +# define EIMSK_BIT _BV(INT7) +# define EICRx_BIT (~(_BV(ISC70) | _BV(ISC71))) +# define SERIAL_PIN_INTERRUPT INT7_vect +# define EICRx EICRB +# elif SOFT_SERIAL_PIN == C7 +# define EIMSK_BIT _BV(INT4) +# define EICRx_BIT (~(_BV(ISC40) | _BV(ISC41))) +# define SERIAL_PIN_INTERRUPT INT4_vect +# define EICRx EICRB +# endif +# endif + +// ATmegaxxU4 specific config +# if defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) +// PE6(INT6) +# if SOFT_SERIAL_PIN == E6 +# define EIMSK_BIT _BV(INT6) +# define EICRx_BIT (~(_BV(ISC60) | _BV(ISC61))) +# define SERIAL_PIN_INTERRUPT INT6_vect +# define EICRx EICRB +# endif +# endif + +// AT90USBxxx specific config +# if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) +// PE4..PE7(INT4..INT7) +# if SOFT_SERIAL_PIN == E4 +# define EIMSK_BIT _BV(INT4) +# define EICRx_BIT (~(_BV(ISC40) | _BV(ISC41))) +# define SERIAL_PIN_INTERRUPT INT4_vect +# define EICRx EICRB +# elif SOFT_SERIAL_PIN == E5 +# define EIMSK_BIT _BV(INT5) +# define EICRx_BIT (~(_BV(ISC50) | _BV(ISC51))) +# define SERIAL_PIN_INTERRUPT INT5_vect +# define EICRx EICRB # elif SOFT_SERIAL_PIN == E6 # define EIMSK_BIT _BV(INT6) # define EICRx_BIT (~(_BV(ISC60) | _BV(ISC61))) # define SERIAL_PIN_INTERRUPT INT6_vect -# else -# error invalid SOFT_SERIAL_PIN value +# define EICRx EICRB +# elif SOFT_SERIAL_PIN == E7 +# define EIMSK_BIT _BV(INT7) +# define EICRx_BIT (~(_BV(ISC70) | _BV(ISC71))) +# define SERIAL_PIN_INTERRUPT INT7_vect +# define EICRx EICRB # endif +# endif -# else -# error serial.c now support ATmega32U4 only +# ifndef SERIAL_PIN_INTERRUPT +# error invalid SOFT_SERIAL_PIN value # endif +# define setPinInputHigh(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) |= _BV((pin)&0xF)) +# define setPinOutput(pin) (DDRx_ADDRESS(pin) |= _BV((pin)&0xF)) +# define writePinHigh(pin) (PORTx_ADDRESS(pin) |= _BV((pin)&0xF)) +# define writePinLow(pin) (PORTx_ADDRESS(pin) &= ~_BV((pin)&0xF)) +# define readPin(pin) ((bool)(PINx_ADDRESS(pin) & _BV((pin)&0xF))) + # define ALWAYS_INLINE __attribute__((always_inline)) # define NO_INLINE __attribute__((noinline)) # define _delay_sub_us(x) __builtin_avr_delay_cycles(x) @@ -210,15 +271,9 @@ void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size) { Transaction_table_size = (uint8_t)sstd_table_size; serial_input_with_pullup(); - // Enable INT0-INT3,INT6 + // Enable INT0-INT7 EIMSK |= EIMSK_BIT; -# if SOFT_SERIAL_PIN == E6 - // Trigger on falling edge of INT6 - EICRB &= EICRx_BIT; -# else - // Trigger on falling edge of INT0-INT3 - EICRA &= EICRx_BIT; -# endif + EICRx &= EICRx_BIT; } // Used by the sender to synchronize timing with the reciver. diff --git a/drivers/chibios/i2c_master.c b/drivers/chibios/i2c_master.c index 4bd8e2af7624..fc4bb2ab3701 100644 --- a/drivers/chibios/i2c_master.c +++ b/drivers/chibios/i2c_master.c @@ -58,18 +58,23 @@ static i2c_status_t chibios_to_qmk(const msg_t* status) { } __attribute__((weak)) void i2c_init(void) { - // Try releasing special pins for a short time - palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, PAL_MODE_INPUT); - palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, PAL_MODE_INPUT); + static bool is_initialised = false; + if (!is_initialised) { + is_initialised = true; - chThdSleepMilliseconds(10); + // Try releasing special pins for a short time + palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, PAL_MODE_INPUT); + palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, PAL_MODE_INPUT); + + chThdSleepMilliseconds(10); #if defined(USE_GPIOV1) - palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, I2C1_SCL_PAL_MODE); - palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, I2C1_SDA_PAL_MODE); + palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, I2C1_SCL_PAL_MODE); + palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, I2C1_SDA_PAL_MODE); #else - palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, PAL_MODE_ALTERNATE(I2C1_SCL_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); - palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, PAL_MODE_ALTERNATE(I2C1_SDA_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); + palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, PAL_MODE_ALTERNATE(I2C1_SCL_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); + palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, PAL_MODE_ALTERNATE(I2C1_SDA_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); #endif + } } i2c_status_t i2c_start(uint8_t address) { diff --git a/drivers/chibios/spi_master.c b/drivers/chibios/spi_master.c index 552ac663c1e9..5aa60742e326 100644 --- a/drivers/chibios/spi_master.c +++ b/drivers/chibios/spi_master.c @@ -22,21 +22,26 @@ static pin_t currentSlavePin = NO_PIN; static SPIConfig spiConfig = {false, NULL, 0, 0, 0, 0}; __attribute__((weak)) void spi_init(void) { - // Try releasing special pins for a short time - palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_INPUT); - palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_INPUT); - palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_INPUT); + static bool is_initialised = false; + if (!is_initialised) { + is_initialised = true; - chThdSleepMilliseconds(10); + // Try releasing special pins for a short time + palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_INPUT); + palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_INPUT); + palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_INPUT); + + chThdSleepMilliseconds(10); #if defined(USE_GPIOV1) - palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); - palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); - palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); + palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); + palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); + palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); #else - palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_ALTERNATE(SPI_SCK_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); - palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_ALTERNATE(SPI_MOSI_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); - palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_ALTERNATE(SPI_MISO_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); + palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_ALTERNATE(SPI_SCK_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); + palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_ALTERNATE(SPI_MOSI_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); + palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_ALTERNATE(SPI_MISO_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); #endif + } } bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) { diff --git a/drivers/eeprom/eeprom_i2c.c b/drivers/eeprom/eeprom_i2c.c index ca8af3da51a4..4210f06f9f9a 100644 --- a/drivers/eeprom/eeprom_i2c.c +++ b/drivers/eeprom/eeprom_i2c.c @@ -42,14 +42,6 @@ # include "debug.h" #endif // DEBUG_EEPROM_OUTPUT -static inline void init_i2c_if_required(void) { - static int done = 0; - if (!done) { - i2c_init(); - done = 1; - } -} - static inline void fill_target_address(uint8_t *buffer, const void *addr) { uintptr_t p = (uintptr_t)addr; for (int i = 0; i < EXTERNAL_EEPROM_ADDRESS_SIZE; ++i) { @@ -58,7 +50,7 @@ static inline void fill_target_address(uint8_t *buffer, const void *addr) { } } -void eeprom_driver_init(void) {} +void eeprom_driver_init(void) { i2c_init(); } void eeprom_driver_erase(void) { #if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT) @@ -80,7 +72,6 @@ void eeprom_read_block(void *buf, const void *addr, size_t len) { uint8_t complete_packet[EXTERNAL_EEPROM_ADDRESS_SIZE]; fill_target_address(complete_packet, addr); - init_i2c_if_required(); i2c_transmit(EXTERNAL_EEPROM_I2C_ADDRESS((uintptr_t)addr), complete_packet, EXTERNAL_EEPROM_ADDRESS_SIZE, 100); i2c_receive(EXTERNAL_EEPROM_I2C_ADDRESS((uintptr_t)addr), buf, len, 100); @@ -98,7 +89,6 @@ void eeprom_write_block(const void *buf, void *addr, size_t len) { uint8_t * read_buf = (uint8_t *)buf; uintptr_t target_addr = (uintptr_t)addr; - init_i2c_if_required(); while (len > 0) { uintptr_t page_offset = target_addr % EXTERNAL_EEPROM_PAGE_SIZE; int write_length = EXTERNAL_EEPROM_PAGE_SIZE - page_offset; diff --git a/drivers/eeprom/eeprom_spi.c b/drivers/eeprom/eeprom_spi.c index 7b6416eafbf4..182731d82f10 100644 --- a/drivers/eeprom/eeprom_spi.c +++ b/drivers/eeprom/eeprom_spi.c @@ -55,14 +55,6 @@ # include "debug.h" #endif // CONSOLE_ENABLE -static void init_spi_if_required(void) { - static int done = 0; - if (!done) { - spi_init(); - done = 1; - } -} - static bool spi_eeprom_start(void) { return spi_start(EXTERNAL_EEPROM_SPI_SLAVE_SELECT_PIN, EXTERNAL_EEPROM_SPI_LSBFIRST, EXTERNAL_EEPROM_SPI_MODE, EXTERNAL_EEPROM_SPI_CLOCK_DIVISOR); } static spi_status_t spi_eeprom_wait_while_busy(int timeout) { @@ -91,7 +83,7 @@ static void spi_eeprom_transmit_address(uintptr_t addr) { //---------------------------------------------------------------------------------------------------------------------- -void eeprom_driver_init(void) {} +void eeprom_driver_init(void) { spi_init(); } void eeprom_driver_erase(void) { #if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT) @@ -110,8 +102,6 @@ void eeprom_driver_erase(void) { } void eeprom_read_block(void *buf, const void *addr, size_t len) { - init_spi_if_required(); - //------------------------------------------------- // Wait for the write-in-progress bit to be cleared bool res = spi_eeprom_start(); @@ -154,8 +144,6 @@ void eeprom_read_block(void *buf, const void *addr, size_t len) { } void eeprom_write_block(const void *buf, void *addr, size_t len) { - init_spi_if_required(); - bool res; uint8_t * read_buf = (uint8_t *)buf; uintptr_t target_addr = (uintptr_t)addr; diff --git a/drivers/haptic/haptic.c b/drivers/haptic/haptic.c index 2ce279b753ae..de3f400527a0 100644 --- a/drivers/haptic/haptic.c +++ b/drivers/haptic/haptic.c @@ -33,11 +33,18 @@ void haptic_init(void) { eeconfig_init(); } haptic_config.raw = eeconfig_read_haptic(); - if (haptic_config.mode < 1) { - haptic_config.mode = 1; - } - if (!haptic_config.mode) { - dprintf("No haptic config found in eeprom, setting default configs\n"); +#ifdef SOLENOID_ENABLE + solenoid_set_dwell(haptic_config.dwell); +#endif + if ((haptic_config.raw == 0) +#ifdef SOLENOID_ENABLE + || (haptic_config.dwell == 0) +#endif + ) { + // this will be called, if the eeprom is not corrupt, + // but the previous firmware didn't have haptic enabled, + // or the previous firmware didn't have solenoid enabled, + // and the current one has solenoid enabled. haptic_reset(); } #ifdef SOLENOID_ENABLE @@ -118,25 +125,37 @@ void haptic_mode_decrease(void) { } void haptic_dwell_increase(void) { - uint8_t dwell = haptic_config.dwell + 1; #ifdef SOLENOID_ENABLE + int16_t next_dwell = ((int16_t)haptic_config.dwell) + SOLENOID_DWELL_STEP_SIZE; if (haptic_config.dwell >= SOLENOID_MAX_DWELL) { - dwell = 1; + // if it's already at max, we wrap back to min + next_dwell = SOLENOID_MIN_DWELL; + } else if (next_dwell > SOLENOID_MAX_DWELL) { + // if we overshoot the max, then cap at max + next_dwell = SOLENOID_MAX_DWELL; } - solenoid_set_dwell(dwell); + solenoid_set_dwell(next_dwell); +#else + int16_t next_dwell = ((int16_t)haptic_config.dwell) + 1; #endif - haptic_set_dwell(dwell); + haptic_set_dwell(next_dwell); } void haptic_dwell_decrease(void) { - uint8_t dwell = haptic_config.dwell - 1; #ifdef SOLENOID_ENABLE - if (haptic_config.dwell < SOLENOID_MIN_DWELL) { - dwell = SOLENOID_MAX_DWELL; + int16_t next_dwell = ((int16_t)haptic_config.dwell) - SOLENOID_DWELL_STEP_SIZE; + if (haptic_config.dwell <= SOLENOID_MIN_DWELL) { + // if it's already at min, we wrap to max + next_dwell = SOLENOID_MAX_DWELL; + } else if (next_dwell < SOLENOID_MIN_DWELL) { + // if we go below min, then we cap to min + next_dwell = SOLENOID_MIN_DWELL; } - solenoid_set_dwell(dwell); + solenoid_set_dwell(next_dwell); +#else + int16_t next_dwell = ((int16_t)haptic_config.dwell) - 1; #endif - haptic_set_dwell(dwell); + haptic_set_dwell(next_dwell); } void haptic_reset(void) { @@ -150,6 +169,12 @@ void haptic_reset(void) { #ifdef SOLENOID_ENABLE uint8_t dwell = SOLENOID_DEFAULT_DWELL; haptic_config.dwell = dwell; + haptic_config.buzz = SOLENOID_DEFAULT_BUZZ; + solenoid_set_dwell(dwell); +#else + // This is to trigger haptic_reset again, if solenoid is enabled in the future. + haptic_config.dwell = 0; + haptic_config.buzz = 0; #endif eeconfig_update_haptic(haptic_config.raw); xprintf("haptic_config.feedback = %u\n", haptic_config.feedback); diff --git a/drivers/haptic/solenoid.c b/drivers/haptic/solenoid.c index d645c379ae97..2975ef893a9f 100644 --- a/drivers/haptic/solenoid.c +++ b/drivers/haptic/solenoid.c @@ -32,14 +32,6 @@ void solenoid_buzz_off(void) { haptic_set_buzz(0); } void solenoid_set_buzz(int buzz) { haptic_set_buzz(buzz); } -void solenoid_dwell_minus(uint8_t solenoid_dwell) { - if (solenoid_dwell > 0) solenoid_dwell--; -} - -void solenoid_dwell_plus(uint8_t solenoid_dwell) { - if (solenoid_dwell < SOLENOID_MAX_DWELL) solenoid_dwell++; -} - void solenoid_set_dwell(uint8_t dwell) { solenoid_dwell = dwell; } void solenoid_stop(void) { @@ -73,7 +65,7 @@ void solenoid_check(void) { // Check whether to buzz the solenoid on and off if (haptic_config.buzz) { - if (elapsed / SOLENOID_MIN_DWELL % 2 == 0) { + if ((elapsed % (SOLENOID_BUZZ_ACTUATED + SOLENOID_BUZZ_NONACTUATED)) < SOLENOID_BUZZ_ACTUATED) { if (!solenoid_buzzing) { solenoid_buzzing = true; writePinHigh(SOLENOID_PIN); diff --git a/drivers/haptic/solenoid.h b/drivers/haptic/solenoid.h index dd6ececa685e..f2a3bc4c3094 100644 --- a/drivers/haptic/solenoid.h +++ b/drivers/haptic/solenoid.h @@ -29,6 +29,22 @@ # define SOLENOID_MIN_DWELL 4 #endif +#ifndef SOLENOID_DWELL_STEP_SIZE +# define SOLENOID_DWELL_STEP_SIZE 1 +#endif + +#ifndef SOLENOID_DEFAULT_BUZZ +# define SOLENOID_DEFAULT_BUZZ 0 +#endif + +#ifndef SOLENOID_BUZZ_ACTUATED +# define SOLENOID_BUZZ_ACTUATED SOLENOID_MIN_DWELL +#endif + +#ifndef SOLENOID_BUZZ_NONACTUATED +# define SOLENOID_BUZZ_NONACTUATED SOLENOID_MIN_DWELL +#endif + #ifndef SOLENOID_PIN # error SOLENOID_PIN not defined #endif @@ -37,8 +53,6 @@ void solenoid_buzz_on(void); void solenoid_buzz_off(void); void solenoid_set_buzz(int buzz); -void solenoid_dwell_minus(uint8_t solenoid_dwell); -void solenoid_dwell_plus(uint8_t solenoid_dwell); void solenoid_set_dwell(uint8_t dwell); void solenoid_stop(void); diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c index ba11db1d2ad2..53bb8ca3fa5b 100644 --- a/drivers/oled/oled_driver.c +++ b/drivers/oled/oled_driver.c @@ -119,6 +119,9 @@ uint32_t oled_timeout; #if OLED_SCROLL_TIMEOUT > 0 uint32_t oled_scroll_timeout; #endif +#if OLED_UPDATE_INTERVAL > 0 +uint16_t oled_update_timeout; +#endif // Internal variables to reduce math instructions @@ -468,8 +471,9 @@ void oled_write_raw_byte(const char data, uint16_t index) { } void oled_write_raw(const char *data, uint16_t size) { - if (size > OLED_MATRIX_SIZE) size = OLED_MATRIX_SIZE; - for (uint16_t i = 0; i < size; i++) { + uint16_t cursor_start_index = oled_cursor - &oled_buffer[0]; + if ((size + cursor_start_index) > OLED_MATRIX_SIZE) size = OLED_MATRIX_SIZE - cursor_start_index; + for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) { if (oled_buffer[i] == data[i]) continue; oled_buffer[i] = data[i]; oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE)); @@ -511,8 +515,9 @@ void oled_write_ln_P(const char *data, bool invert) { } void oled_write_raw_P(const char *data, uint16_t size) { - if (size > OLED_MATRIX_SIZE) size = OLED_MATRIX_SIZE; - for (uint16_t i = 0; i < size; i++) { + uint16_t cursor_start_index = oled_cursor - &oled_buffer[0]; + if ((size + cursor_start_index) > OLED_MATRIX_SIZE) size = OLED_MATRIX_SIZE - cursor_start_index; + for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) { uint8_t c = pgm_read_byte(data++); if (oled_buffer[i] == c) continue; oled_buffer[i] = c; @@ -650,9 +655,16 @@ void oled_task(void) { return; } +#if OLED_UPDATE_INTERVAL > 0 + if (timer_elapsed(oled_update_timeout) >= OLED_UPDATE_INTERVAL) { + oled_update_timeout = timer_read(); + oled_set_cursor(0, 0); + oled_task_user(); + } +#else oled_set_cursor(0, 0); - oled_task_user(); +#endif #if OLED_SCROLL_TIMEOUT > 0 if (oled_dirty && oled_scrolling) { diff --git a/layouts/community/ortho_4x12/drashna/rules.mk b/layouts/community/ortho_4x12/drashna/rules.mk index 3383cfd35f3b..2a9576758f31 100644 --- a/layouts/community/ortho_4x12/drashna/rules.mk +++ b/layouts/community/ortho_4x12/drashna/rules.mk @@ -34,7 +34,7 @@ ifeq ($(strip $(KEYBOARD)), planck/ez) RGBLIGHT_ENABLE = no # SERIAL_LINK_ENABLE = yes ENCODER_ENABLE = yes - RGB_MATRIX_ENABLE = IS31FL3737 + RGB_MATRIX_ENABLE = yes INDICATOR_LIGHTS = yes RGBLIGHT_TWINKLE = yes RGBLIGHT_STARTUP_ANIMATION = yes diff --git a/layouts/community/split_3x6_3/bcat/rules.mk b/layouts/community/split_3x6_3/bcat/rules.mk index 3a9796673eae..caf6ec7ece75 100644 --- a/layouts/community/split_3x6_3/bcat/rules.mk +++ b/layouts/community/split_3x6_3/bcat/rules.mk @@ -6,5 +6,5 @@ ifeq ($(strip $(KEYBOARD)), crkbd/rev1/common) else ifeq ($(strip $(KEYBOARD)), crkbd/rev1/legacy) BOOTLOADER = atmel-dfu # Elite-C - RGB_MATRIX_ENABLE = WS2812 # per-key RGB and underglow + RGB_MATRIX_ENABLE = yes # per-key RGB and underglow endif diff --git a/layouts/community/split_3x6_3/drashna/rules.mk b/layouts/community/split_3x6_3/drashna/rules.mk index 6ac1554b330a..46f543f4e8fc 100644 --- a/layouts/community/split_3x6_3/drashna/rules.mk +++ b/layouts/community/split_3x6_3/drashna/rules.mk @@ -22,7 +22,7 @@ SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend ifeq ($(strip $(KEYBOARD)), crkbd/rev1) OLED_DRIVER_ENABLE = yes - RGB_MATRIX_ENABLE = WS2812 + RGB_MATRIX_ENABLE = yes HAPTIC_ENABLE = SOLENOID BOOTLOADER = qmk-dfu endif diff --git a/lib/python/milc.py b/lib/python/milc.py deleted file mode 100644 index 0cdd43dc893a..000000000000 --- a/lib/python/milc.py +++ /dev/null @@ -1,826 +0,0 @@ -#!/usr/bin/env python3 -# coding=utf-8 -"""MILC - A CLI Framework - -PYTHON_ARGCOMPLETE_OK - -MILC is an opinionated framework for writing CLI apps. It optimizes for the -most common unix tool pattern- small tools that are run from the command -line but generally do not feature any user interaction while they run. - -For more details see the MILC documentation: - - -""" -from __future__ import division, print_function, unicode_literals -import argparse -import logging -import os -import re -import shlex -import subprocess -import sys -from decimal import Decimal -from pathlib import Path -from platform import platform -from tempfile import NamedTemporaryFile -from time import sleep - -try: - from ConfigParser import RawConfigParser -except ImportError: - from configparser import RawConfigParser - -try: - import thread - import threading -except ImportError: - thread = None - -import argcomplete -import colorama -from appdirs import user_config_dir - -# Disable logging until we can configure it how the user wants -logging.basicConfig(stream=os.devnull) - -# Log Level Representations -EMOJI_LOGLEVELS = { - 'CRITICAL': '{bg_red}{fg_white}¬_¬{style_reset_all}', - 'ERROR': '{fg_red}☒{style_reset_all}', - 'WARNING': '{fg_yellow}⚠{style_reset_all}', - 'INFO': '{fg_blue}ℹ{style_reset_all}', - 'DEBUG': '{fg_cyan}☐{style_reset_all}', - 'NOTSET': '{style_reset_all}¯\\_(o_o)_/¯' -} -EMOJI_LOGLEVELS['FATAL'] = EMOJI_LOGLEVELS['CRITICAL'] -EMOJI_LOGLEVELS['WARN'] = EMOJI_LOGLEVELS['WARNING'] -UNICODE_SUPPORT = sys.stdout.encoding.lower().startswith('utf') - -# ANSI Color setup -# Regex was gratefully borrowed from kfir on stackoverflow: -# https://stackoverflow.com/a/45448194 -ansi_regex = r'\x1b(' \ - r'(\[\??\d+[hl])|' \ - r'([=<>a-kzNM78])|' \ - r'([\(\)][a-b0-2])|' \ - r'(\[\d{0,2}[ma-dgkjqi])|' \ - r'(\[\d+;\d+[hfy]?)|' \ - r'(\[;?[hf])|' \ - r'(#[3-68])|' \ - r'([01356]n)|' \ - r'(O[mlnp-z]?)|' \ - r'(/Z)|' \ - r'(\d+)|' \ - r'(\[\?\d;\d0c)|' \ - r'(\d;\dR))' -ansi_escape = re.compile(ansi_regex, flags=re.IGNORECASE) -ansi_styles = ( - ('fg', colorama.ansi.AnsiFore()), - ('bg', colorama.ansi.AnsiBack()), - ('style', colorama.ansi.AnsiStyle()), -) -ansi_colors = {} - -for prefix, obj in ansi_styles: - for color in [x for x in obj.__dict__ if not x.startswith('_')]: - ansi_colors[prefix + '_' + color.lower()] = getattr(obj, color) - - -def format_ansi(text): - """Return a copy of text with certain strings replaced with ansi. - """ - # Avoid .format() so we don't have to worry about the log content - for color in ansi_colors: - text = text.replace('{%s}' % color, ansi_colors[color]) - return text + ansi_colors['style_reset_all'] - - -class ANSIFormatterMixin(object): - """A log formatter mixin that inserts ANSI color. - """ - def format(self, record): - msg = super(ANSIFormatterMixin, self).format(record) - return format_ansi(msg) - - -class ANSIStrippingMixin(object): - """A log formatter mixin that strips ANSI. - """ - def format(self, record): - msg = super(ANSIStrippingMixin, self).format(record) - record.levelname = ansi_escape.sub('', record.levelname) - return ansi_escape.sub('', msg) - - -class EmojiLoglevelMixin(object): - """A log formatter mixin that makes the loglevel an emoji on UTF capable terminals. - """ - def format(self, record): - if UNICODE_SUPPORT: - record.levelname = EMOJI_LOGLEVELS[record.levelname].format(**ansi_colors) - return super(EmojiLoglevelMixin, self).format(record) - - -class ANSIFormatter(ANSIFormatterMixin, logging.Formatter): - """A log formatter that colorizes output. - """ - pass - - -class ANSIStrippingFormatter(ANSIStrippingMixin, ANSIFormatterMixin, logging.Formatter): - """A log formatter that strips ANSI - """ - pass - - -class ANSIEmojiLoglevelFormatter(EmojiLoglevelMixin, ANSIFormatterMixin, logging.Formatter): - """A log formatter that adds Emoji and ANSI - """ - pass - - -class ANSIStrippingEmojiLoglevelFormatter(ANSIStrippingMixin, EmojiLoglevelMixin, ANSIFormatterMixin, logging.Formatter): - """A log formatter that adds Emoji and strips ANSI - """ - pass - - -class Configuration(object): - """Represents the running configuration. - - This class never raises IndexError, instead it will return None if a - section or option does not yet exist. - """ - def __contains__(self, key): - return self._config.__contains__(key) - - def __iter__(self): - return self._config.__iter__() - - def __len__(self): - return self._config.__len__() - - def __repr__(self): - return self._config.__repr__() - - def keys(self): - return self._config.keys() - - def items(self): - return self._config.items() - - def values(self): - return self._config.values() - - def __init__(self, *args, **kwargs): - self._config = {} - - def __getattr__(self, key): - return self.__getitem__(key) - - def __getitem__(self, key): - """Returns a config section, creating it if it doesn't exist yet. - """ - if key not in self._config: - self.__dict__[key] = self._config[key] = ConfigurationSection(self) - - return self._config[key] - - def __setitem__(self, key, value): - self.__dict__[key] = value - self._config[key] = value - - def __delitem__(self, key): - if key in self.__dict__ and key[0] != '_': - del self.__dict__[key] - if key in self._config: - del self._config[key] - - -class ConfigurationSection(Configuration): - def __init__(self, parent, *args, **kwargs): - super(ConfigurationSection, self).__init__(*args, **kwargs) - self.parent = parent - - def __getitem__(self, key): - """Returns a config value, pulling from the `user` section as a fallback. - This is called when the attribute is accessed either via the get method or through [ ] index. - """ - if key in self._config and self._config.get(key) is not None: - return self._config[key] - - elif key in self.parent.user: - return self.parent.user[key] - - return None - - def __getattr__(self, key): - """Returns the config value from the `user` section. - This is called when the attribute is accessed via dot notation but does not exists. - """ - if key in self.parent.user: - return self.parent.user[key] - - return None - - -def handle_store_boolean(self, *args, **kwargs): - """Does the add_argument for action='store_boolean'. - """ - disabled_args = None - disabled_kwargs = kwargs.copy() - disabled_kwargs['action'] = 'store_false' - disabled_kwargs['dest'] = self.get_argument_name(*args, **kwargs) - disabled_kwargs['help'] = 'Disable ' + kwargs['help'] - kwargs['action'] = 'store_true' - kwargs['help'] = 'Enable ' + kwargs['help'] - - for flag in args: - if flag[:2] == '--': - disabled_args = ('--no-' + flag[2:],) - break - - self.add_argument(*args, **kwargs) - self.add_argument(*disabled_args, **disabled_kwargs) - - return (args, kwargs, disabled_args, disabled_kwargs) - - -class SubparserWrapper(object): - """Wrap subparsers so we can track what options the user passed. - """ - def __init__(self, cli, submodule, subparser): - self.cli = cli - self.submodule = submodule - self.subparser = subparser - - for attr in dir(subparser): - if not hasattr(self, attr): - setattr(self, attr, getattr(subparser, attr)) - - def completer(self, completer): - """Add an arpcomplete completer to this subcommand. - """ - self.subparser.completer = completer - - def add_argument(self, *args, **kwargs): - """Add an argument for this subcommand. - - This also stores the default for the argument in `self.cli.default_arguments`. - """ - if kwargs.get('action') == 'store_boolean': - # Store boolean will call us again with the enable/disable flag arguments - return handle_store_boolean(self, *args, **kwargs) - - self.cli.acquire_lock() - argument_name = self.cli.get_argument_name(*args, **kwargs) - - self.subparser.add_argument(*args, **kwargs) - - if kwargs.get('action') == 'store_false': - self.cli._config_store_false.append(argument_name) - - if kwargs.get('action') == 'store_true': - self.cli._config_store_true.append(argument_name) - - if self.submodule not in self.cli.default_arguments: - self.cli.default_arguments[self.submodule] = {} - self.cli.default_arguments[self.submodule][argument_name] = kwargs.get('default') - self.cli.release_lock() - - -class MILC(object): - """MILC - An Opinionated Batteries Included Framework - """ - def __init__(self): - """Initialize the MILC object. - - version - The version string to associate with your CLI program - """ - # Setup a lock for thread safety - self._lock = threading.RLock() if thread else None - - # Define some basic info - self.acquire_lock() - self._config_store_true = [] - self._config_store_false = [] - self._description = None - self._entrypoint = None - self._inside_context_manager = False - self.ansi = ansi_colors - self.arg_only = {} - self.config = self.config_source = None - self.config_file = None - self.default_arguments = {} - self.version = 'unknown' - self.platform = platform() - - # Figure out our program name - self.prog_name = sys.argv[0][:-3] if sys.argv[0].endswith('.py') else sys.argv[0] - self.prog_name = self.prog_name.split('/')[-1] - self.release_lock() - - # Initialize all the things - self.read_config_file() - self.initialize_argparse() - self.initialize_logging() - - @property - def description(self): - return self._description - - @description.setter - def description(self, value): - self._description = self._arg_parser.description = value - - def echo(self, text, *args, **kwargs): - """Print colorized text to stdout. - - ANSI color strings (such as {fg-blue}) will be converted into ANSI - escape sequences, and the ANSI reset sequence will be added to all - strings. - - If *args or **kwargs are passed they will be used to %-format the strings. - - If `self.config.general.color` is False any ANSI escape sequences in the text will be stripped. - """ - if args and kwargs: - raise RuntimeError('You can only specify *args or **kwargs, not both!') - - args = args or kwargs - text = format_ansi(text) - - if not self.config.general.color: - text = ansi_escape.sub('', text) - - print(text % args) - - def run(self, command, *args, **kwargs): - """Run a command with subprocess.run - The *args and **kwargs arguments get passed directly to `subprocess.run`. - """ - if isinstance(command, str): - raise TypeError('`command` must be a non-text sequence such as list or tuple.') - - if 'windows' in self.platform.lower(): - safecmd = map(shlex.quote, command) - safecmd = ' '.join(safecmd) - command = [os.environ['SHELL'], '-c', safecmd] - - self.log.debug('Running command: %s', command) - - return subprocess.run(command, *args, **kwargs) - - def initialize_argparse(self): - """Prepare to process arguments from sys.argv. - """ - kwargs = { - 'fromfile_prefix_chars': '@', - 'conflict_handler': 'resolve', - } - - self.acquire_lock() - self.subcommands = {} - self._subparsers = None - self.argwarn = argcomplete.warn - self.args = None - self._arg_parser = argparse.ArgumentParser(**kwargs) - self.set_defaults = self._arg_parser.set_defaults - self.print_usage = self._arg_parser.print_usage - self.print_help = self._arg_parser.print_help - self.release_lock() - - def completer(self, completer): - """Add an argcomplete completer to this subcommand. - """ - self._arg_parser.completer = completer - - def add_argument(self, *args, **kwargs): - """Wrapper to add arguments and track whether they were passed on the command line. - """ - if 'action' in kwargs and kwargs['action'] == 'store_boolean': - return handle_store_boolean(self, *args, **kwargs) - - self.acquire_lock() - - self._arg_parser.add_argument(*args, **kwargs) - if 'general' not in self.default_arguments: - self.default_arguments['general'] = {} - self.default_arguments['general'][self.get_argument_name(*args, **kwargs)] = kwargs.get('default') - - self.release_lock() - - def initialize_logging(self): - """Prepare the defaults for the logging infrastructure. - """ - self.acquire_lock() - self.log_file = None - self.log_file_mode = 'a' - self.log_file_handler = None - self.log_print = True - self.log_print_to = sys.stderr - self.log_print_level = logging.INFO - self.log_file_level = logging.DEBUG - self.log_level = logging.INFO - self.log = logging.getLogger(self.__class__.__name__) - self.log.setLevel(logging.DEBUG) - logging.root.setLevel(logging.DEBUG) - self.release_lock() - - self.add_argument('-V', '--version', version=self.version, action='version', help='Display the version and exit') - self.add_argument('-v', '--verbose', action='store_true', help='Make the logging more verbose') - self.add_argument('--datetime-fmt', default='%Y-%m-%d %H:%M:%S', help='Format string for datetimes') - self.add_argument('--log-fmt', default='%(levelname)s %(message)s', help='Format string for printed log output') - self.add_argument('--log-file-fmt', default='[%(levelname)s] [%(asctime)s] [file:%(pathname)s] [line:%(lineno)d] %(message)s', help='Format string for log file.') - self.add_argument('--log-file', help='File to write log messages to') - self.add_argument('--color', action='store_boolean', default=True, help='color in output') - self.add_argument('--config-file', help='The location for the configuration file') - self.arg_only['config_file'] = ['general'] - - def add_subparsers(self, title='Sub-commands', **kwargs): - if self._inside_context_manager: - raise RuntimeError('You must run this before the with statement!') - - self.acquire_lock() - self._subparsers = self._arg_parser.add_subparsers(title=title, dest='subparsers', **kwargs) - self.release_lock() - - def acquire_lock(self): - """Acquire the MILC lock for exclusive access to properties. - """ - if self._lock: - self._lock.acquire() - - def release_lock(self): - """Release the MILC lock. - """ - if self._lock: - self._lock.release() - - def find_config_file(self): - """Locate the config file. - """ - if self.config_file: - return self.config_file - - if '--config-file' in sys.argv: - return Path(sys.argv[sys.argv.index('--config-file') + 1]).expanduser().resolve() - - filedir = user_config_dir(appname='qmk', appauthor='QMK') - filename = '%s.ini' % self.prog_name - return Path(filedir) / filename - - def get_argument_name(self, *args, **kwargs): - """Takes argparse arguments and returns the dest name. - """ - try: - return self._arg_parser._get_optional_kwargs(*args, **kwargs)['dest'] - except ValueError: - return self._arg_parser._get_positional_kwargs(*args, **kwargs)['dest'] - - def argument(self, *args, **kwargs): - """Decorator to call self.add_argument or self..add_argument. - """ - if self._inside_context_manager: - raise RuntimeError('You must run this before the with statement!') - - def argument_function(handler): - subcommand_name = handler.__name__.replace("_", "-") - - if kwargs.get('arg_only'): - arg_name = self.get_argument_name(*args, **kwargs) - if arg_name not in self.arg_only: - self.arg_only[arg_name] = [] - self.arg_only[arg_name].append(subcommand_name) - del kwargs['arg_only'] - - if handler is self._entrypoint: - self.add_argument(*args, **kwargs) - - elif subcommand_name in self.subcommands: - self.subcommands[subcommand_name].add_argument(*args, **kwargs) - - else: - raise RuntimeError('Decorated function is not entrypoint or subcommand!') - - return handler - - return argument_function - - def arg_passed(self, arg): - """Returns True if arg was passed on the command line. - """ - return self.default_arguments.get(arg) != self.args[arg] - - def parse_args(self): - """Parse the CLI args. - """ - if self.args: - self.log.debug('Warning: Arguments have already been parsed, ignoring duplicate attempt!') - return - - argcomplete.autocomplete(self._arg_parser) - - self.acquire_lock() - self.args = self._arg_parser.parse_args() - - if 'entrypoint' in self.args: - self._entrypoint = self.args.entrypoint - - self.release_lock() - - def read_config_file(self): - """Read in the configuration file and store it in self.config. - """ - self.acquire_lock() - self.config = Configuration() - self.config_source = Configuration() - self.config_file = self.find_config_file() - - if self.config_file and self.config_file.exists(): - config = RawConfigParser(self.config) - config.read(str(self.config_file)) - - # Iterate over the config file options and write them into self.config - for section in config.sections(): - for option in config.options(section): - value = config.get(section, option) - - # Coerce values into useful datatypes - if value.lower() in ['1', 'yes', 'true', 'on']: - value = True - elif value.lower() in ['0', 'no', 'false', 'off']: - value = False - elif value.lower() in ['none']: - continue - elif value.replace('.', '').isdigit(): - if '.' in value: - value = Decimal(value) - else: - value = int(value) - - self.config[section][option] = value - self.config_source[section][option] = 'config_file' - - self.release_lock() - - def merge_args_into_config(self): - """Merge CLI arguments into self.config to create the runtime configuration. - """ - self.acquire_lock() - for argument in vars(self.args): - if argument in ('subparsers', 'entrypoint'): - continue - - # Find the argument's section - # Underscores in command's names are converted to dashes during initialization. - # TODO(Erovia) Find a better solution - entrypoint_name = self._entrypoint.__name__.replace("_", "-") - if entrypoint_name in self.default_arguments and argument in self.default_arguments[entrypoint_name]: - argument_found = True - section = self._entrypoint.__name__ - if argument in self.default_arguments['general']: - argument_found = True - section = 'general' - - if not argument_found: - raise RuntimeError('Could not find argument in `self.default_arguments`. This should be impossible!') - exit(1) - - if argument not in self.arg_only or section not in self.arg_only[argument]: - # Determine the arg value and source - arg_value = getattr(self.args, argument) - if argument in self._config_store_true and arg_value: - passed_on_cmdline = True - elif argument in self._config_store_false and not arg_value: - passed_on_cmdline = True - elif arg_value is not None: - passed_on_cmdline = True - else: - passed_on_cmdline = False - - # Merge this argument into self.config - if passed_on_cmdline and (argument in self.default_arguments['general'] or argument in self.default_arguments[entrypoint_name] or argument not in self.config[entrypoint_name]): - self.config[section][argument] = arg_value - self.config_source[section][argument] = 'argument' - - self.release_lock() - - def save_config(self): - """Save the current configuration to the config file. - """ - self.log.debug("Saving config file to '%s'", str(self.config_file)) - - if not self.config_file: - self.log.warning('%s.config_file file not set, not saving config!', self.__class__.__name__) - return - - self.acquire_lock() - - # Generate a sanitized version of our running configuration - config = RawConfigParser() - for section_name, section in self.config._config.items(): - config.add_section(section_name) - for option_name, value in section.items(): - if section_name == 'general': - if option_name in ['config_file']: - continue - if value is not None: - config.set(section_name, option_name, str(value)) - - # Write out the config file - config_dir = self.config_file.parent - if not config_dir.exists(): - config_dir.mkdir(parents=True, exist_ok=True) - - with NamedTemporaryFile(mode='w', dir=str(config_dir), delete=False) as tmpfile: - config.write(tmpfile) - - # Move the new config file into place atomically - if os.path.getsize(tmpfile.name) > 0: - os.replace(tmpfile.name, str(self.config_file)) - else: - self.log.warning('Config file saving failed, not replacing %s with %s.', str(self.config_file), tmpfile.name) - - # Housekeeping - self.release_lock() - cli.log.info('Wrote configuration to %s', shlex.quote(str(self.config_file))) - - def __call__(self): - """Execute the entrypoint function. - """ - if not self._inside_context_manager: - # If they didn't use the context manager use it ourselves - with self: - return self.__call__() - - if not self._entrypoint: - raise RuntimeError('No entrypoint provided!') - - return self._entrypoint(self) - - def entrypoint(self, description): - """Set the entrypoint for when no subcommand is provided. - """ - if self._inside_context_manager: - raise RuntimeError('You must run this before cli()!') - - self.acquire_lock() - self.description = description - self.release_lock() - - def entrypoint_func(handler): - self.acquire_lock() - self._entrypoint = handler - self.release_lock() - - return handler - - return entrypoint_func - - def add_subcommand(self, handler, description, name=None, hidden=False, **kwargs): - """Register a subcommand. - - If name is not provided we use `handler.__name__`. - """ - - if self._inside_context_manager: - raise RuntimeError('You must run this before the with statement!') - - if self._subparsers is None: - self.add_subparsers(metavar="") - - if not name: - name = handler.__name__.replace("_", "-") - - self.acquire_lock() - if not hidden: - self._subparsers.metavar = "{%s,%s}" % (self._subparsers.metavar[1:-1], name) if self._subparsers.metavar else "{%s%s}" % (self._subparsers.metavar[1:-1], name) - kwargs['help'] = description - self.subcommands[name] = SubparserWrapper(self, name, self._subparsers.add_parser(name, **kwargs)) - self.subcommands[name].set_defaults(entrypoint=handler) - - self.release_lock() - - return handler - - def subcommand(self, description, hidden=False, **kwargs): - """Decorator to register a subcommand. - """ - def subcommand_function(handler): - return self.add_subcommand(handler, description, hidden=hidden, **kwargs) - - return subcommand_function - - def setup_logging(self): - """Called by __enter__() to setup the logging configuration. - """ - if len(logging.root.handlers) != 0: - # MILC is the only thing that should have root log handlers - logging.root.handlers = [] - - self.acquire_lock() - - if self.config['general']['verbose']: - self.log_print_level = logging.DEBUG - - self.log_file = self.config['general']['log_file'] or self.log_file - self.log_file_format = ANSIStrippingFormatter(self.config['general']['log_file_fmt'], self.config['general']['datetime_fmt']) - self.log_format = self.config['general']['log_fmt'] - - if self.config.general.color: - self.log_format = ANSIEmojiLoglevelFormatter(self.config.general.log_fmt, self.config.general.datetime_fmt) - else: - self.log_format = ANSIStrippingEmojiLoglevelFormatter(self.config.general.log_fmt, self.config.general.datetime_fmt) - - if self.log_file: - self.log_file_handler = logging.FileHandler(self.log_file, self.log_file_mode) - self.log_file_handler.setLevel(self.log_file_level) - self.log_file_handler.setFormatter(self.log_file_format) - logging.root.addHandler(self.log_file_handler) - - if self.log_print: - self.log_print_handler = logging.StreamHandler(self.log_print_to) - self.log_print_handler.setLevel(self.log_print_level) - self.log_print_handler.setFormatter(self.log_format) - logging.root.addHandler(self.log_print_handler) - - self.release_lock() - - def __enter__(self): - if self._inside_context_manager: - self.log.debug('Warning: context manager was entered again. This usually means that self.__call__() was called before the with statement. You probably do not want to do that.') - return - - self.acquire_lock() - self._inside_context_manager = True - self.release_lock() - - colorama.init() - self.parse_args() - self.merge_args_into_config() - self.setup_logging() - - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.acquire_lock() - self._inside_context_manager = False - self.release_lock() - - if exc_type is not None and not isinstance(SystemExit(), exc_type): - print(exc_type) - logging.exception(exc_val) - exit(255) - - -cli = MILC() - -if __name__ == '__main__': - - @cli.argument('-c', '--comma', help='comma in output', default=True, action='store_boolean') - @cli.entrypoint('My useful CLI tool with subcommands.') - def main(cli): - comma = ',' if cli.config.general.comma else '' - cli.log.info('{bg_green}{fg_red}Hello%s World!', comma) - - @cli.argument('-n', '--name', help='Name to greet', default='World') - @cli.subcommand('Description of hello subcommand here.') - def hello(cli): - comma = ',' if cli.config.general.comma else '' - cli.log.info('{fg_blue}Hello%s %s!', comma, cli.config.hello.name) - - def goodbye(cli): - comma = ',' if cli.config.general.comma else '' - cli.log.info('{bg_red}Goodbye%s %s!', comma, cli.config.goodbye.name) - - @cli.argument('-n', '--name', help='Name to greet', default='World') - @cli.subcommand('Think a bit before greeting the user.') - def thinking(cli): - comma = ',' if cli.config.general.comma else '' - spinner = cli.spinner(text='Just a moment...', spinner='earth') - spinner.start() - sleep(2) - spinner.stop() - - with cli.spinner(text='Almost there!', spinner='moon'): - sleep(2) - - cli.log.info('{fg_cyan}Hello%s %s!', comma, cli.config.thinking.name) - - @cli.subcommand('Show off our ANSI colors.') - def pride(cli): - cli.echo('{bg_red} ') - cli.echo('{bg_lightred_ex} ') - cli.echo('{bg_lightyellow_ex} ') - cli.echo('{bg_green} ') - cli.echo('{bg_blue} ') - cli.echo('{bg_magenta} ') - - # You can register subcommands using decorators as seen above, or using functions like like this: - cli.add_subcommand(goodbye, 'This will show up in --help output.') - cli.goodbye.add_argument('-n', '--name', help='Name to bid farewell to', default='World') - - cli() # Automatically picks between main(), hello() and goodbye() diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index 77724a244800..10536bb230ba 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -8,6 +8,7 @@ from . import c2json from . import cformat +from . import chibios from . import clean from . import compile from . import config diff --git a/lib/python/qmk/cli/chibios/__init__.py b/lib/python/qmk/cli/chibios/__init__.py new file mode 100644 index 000000000000..4301837defda --- /dev/null +++ b/lib/python/qmk/cli/chibios/__init__.py @@ -0,0 +1 @@ +from . import confmigrate diff --git a/lib/python/qmk/cli/chibios/confmigrate.py b/lib/python/qmk/cli/chibios/confmigrate.py new file mode 100644 index 000000000000..eae294a0c69b --- /dev/null +++ b/lib/python/qmk/cli/chibios/confmigrate.py @@ -0,0 +1,161 @@ +"""This script automates the copying of the default keymap into your own keymap. +""" +import re +import sys +import os + +from qmk.constants import QMK_FIRMWARE +from qmk.path import normpath +from milc import cli + + +def eprint(*args, **kwargs): + print(*args, file=sys.stderr, **kwargs) + + +fileHeader = """\ +/* Copyright 2020 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * This file was auto-generated by: + * `qmk chibios-confupdate -i {0} -r {1}` + */ + +#pragma once +""" + + +def collect_defines(filepath): + with open(filepath, 'r') as f: + content = f.read() + define_search = re.compile(r'(?m)^#\s*define\s+(?:.*\\\r?\n)*.*$', re.MULTILINE) + value_search = re.compile(r'^#\s*define\s+(?P[a-zA-Z0-9_]+(\([^\)]*\))?)\s*(?P.*)', re.DOTALL) + define_matches = define_search.findall(content) + + defines = {"keys": [], "dict": {}} + for define_match in define_matches: + value_match = value_search.search(define_match) + defines["keys"].append(value_match.group("name")) + defines["dict"][value_match.group("name")] = value_match.group("value") + return defines + + +def check_diffs(input_defs, reference_defs): + not_present_in_input = [] + not_present_in_reference = [] + to_override = [] + + for key in reference_defs["keys"]: + if key not in input_defs["dict"]: + not_present_in_input.append(key) + continue + + for key in input_defs["keys"]: + if key not in input_defs["dict"]: + not_present_in_input.append(key) + continue + + for key in input_defs["keys"]: + if key in reference_defs["keys"] and input_defs["dict"][key] != reference_defs["dict"][key]: + to_override.append((key, input_defs["dict"][key])) + + return (to_override, not_present_in_input, not_present_in_reference) + + +def migrate_chconf_h(to_override, outfile): + print(fileHeader.format(cli.args.input.relative_to(QMK_FIRMWARE), cli.args.reference.relative_to(QMK_FIRMWARE)), file=outfile) + + for override in to_override: + print("#define %s %s" % (override[0], override[1]), file=outfile) + print("", file=outfile) + + print("#include_next \n", file=outfile) + + +def migrate_halconf_h(to_override, outfile): + print(fileHeader.format(cli.args.input.relative_to(QMK_FIRMWARE), cli.args.reference.relative_to(QMK_FIRMWARE)), file=outfile) + + for override in to_override: + print("#define %s %s" % (override[0], override[1]), file=outfile) + print("", file=outfile) + + print("#include_next \n", file=outfile) + + +def migrate_mcuconf_h(to_override, outfile): + print(fileHeader.format(cli.args.input.relative_to(QMK_FIRMWARE), cli.args.reference.relative_to(QMK_FIRMWARE)), file=outfile) + + print("#include_next \n", file=outfile) + + for override in to_override: + print("#undef %s" % (override[0]), file=outfile) + print("#define %s %s" % (override[0], override[1]), file=outfile) + print("", file=outfile) + + +@cli.argument('-i', '--input', type=normpath, arg_only=True, help='Specify input config file.') +@cli.argument('-r', '--reference', type=normpath, arg_only=True, help='Specify the reference file to compare against') +@cli.argument('-o', '--overwrite', arg_only=True, action='store_true', help='Overwrites the input file during migration.') +@cli.argument('-d', '--delete', arg_only=True, action='store_true', help='If the file has no overrides, migration will delete the input file.') +@cli.subcommand('Generates a migrated ChibiOS configuration file, as a result of comparing the input against a reference') +def chibios_confmigrate(cli): + """Generates a usable ChibiOS replacement configuration file, based on a fully-defined conf and a reference config. + """ + + input_defs = collect_defines(cli.args.input) + reference_defs = collect_defines(cli.args.reference) + + (to_override, not_present_in_input, not_present_in_reference) = check_diffs(input_defs, reference_defs) + + if len(not_present_in_input) > 0: + eprint("Keys not in input, but present inside reference (potential manual migration required):") + for key in not_present_in_input: + eprint(" %s" % (key)) + + if len(not_present_in_reference) > 0: + eprint("Keys not in reference, but present inside input (potential manual migration required):") + for key in not_present_in_reference: + eprint(" %s" % (key)) + + if len(to_override) == 0: + eprint('No overrides found! If there were no missing keys above, it should be safe to delete the input file.') + if cli.args.delete: + os.remove(cli.args.input) + else: + eprint('Overrides found:') + for override in to_override: + eprint("%40s: %s -> %s" % (override[0], reference_defs["dict"][override[0]].encode('unicode_escape').decode("utf-8"), override[1].encode('unicode_escape').decode("utf-8"))) + + eprint('--------------------------------------') + + if "CHCONF_H" in input_defs["dict"] or "_CHCONF_H_" in input_defs["dict"]: + migrate_chconf_h(to_override, outfile=sys.stdout) + if cli.args.overwrite: + with open(cli.args.input, "w") as out_file: + migrate_chconf_h(to_override, outfile=out_file) + + elif "HALCONF_H" in input_defs["dict"] or "_HALCONF_H_" in input_defs["dict"]: + migrate_halconf_h(to_override, outfile=sys.stdout) + if cli.args.overwrite: + with open(cli.args.input, "w") as out_file: + migrate_halconf_h(to_override, outfile=out_file) + + elif "MCUCONF_H" in input_defs["dict"] or "_MCUCONF_H_" in input_defs["dict"]: + migrate_mcuconf_h(to_override, outfile=sys.stdout) + if cli.args.overwrite: + with open(cli.args.input, "w") as out_file: + migrate_mcuconf_h(to_override, outfile=out_file) diff --git a/lib/python/qmk/cli/doctor.py b/lib/python/qmk/cli/doctor.py index a5eda555f0cc..4fe318b636a5 100755 --- a/lib/python/qmk/cli/doctor.py +++ b/lib/python/qmk/cli/doctor.py @@ -10,9 +10,9 @@ from enum import Enum from milc import cli +from milc.questions import yesno from qmk import submodules from qmk.constants import QMK_FIRMWARE -from qmk.questions import yesno from qmk.commands import run diff --git a/lib/python/qmk/questions.py b/lib/python/qmk/questions.py deleted file mode 100644 index 865c6bbdc513..000000000000 --- a/lib/python/qmk/questions.py +++ /dev/null @@ -1,183 +0,0 @@ -"""Functions to collect user input. -""" - -from milc import cli - -try: - from milc import format_ansi -except ImportError: - from milc.ansi import format_ansi - - -def yesno(prompt, *args, default=None, **kwargs): - """Displays prompt to the user and gets a yes or no response. - - Returns True for a yes and False for a no. - - If you add `--yes` and `--no` arguments to your program the user can answer questions by passing command line flags. - - @add_argument('-y', '--yes', action='store_true', arg_only=True, help='Answer yes to all questions.') - @add_argument('-n', '--no', action='store_true', arg_only=True, help='Answer no to all questions.') - - Arguments: - prompt - The prompt to present to the user. Can include ANSI and format strings like milc's `cli.echo()`. - - default - Whether to default to a Yes or No when the user presses enter. - - None- force the user to enter Y or N - - True- Default to yes - - False- Default to no - """ - if not args and kwargs: - args = kwargs - - if 'no' in cli.args and cli.args.no: - return False - - if 'yes' in cli.args and cli.args.yes: - return True - - if default is not None: - if default: - prompt = prompt + ' [Y/n] ' - else: - prompt = prompt + ' [y/N] ' - - while True: - cli.echo('') - answer = input(format_ansi(prompt % args)) - cli.echo('') - - if not answer and prompt is not None: - return default - - elif answer.lower() in ['y', 'yes']: - return True - - elif answer.lower() in ['n', 'no']: - return False - - -def question(prompt, *args, default=None, confirm=False, answer_type=str, validate=None, **kwargs): - """Prompt the user to answer a question with a free-form input. - - Arguments: - prompt - The prompt to present to the user. Can include ANSI and format strings like milc's `cli.echo()`. - - default - The value to return when the user doesn't enter any value. Use None to prompt until they enter a value. - - confirm - Present the user with a confirmation dialog before accepting their answer. - - answer_type - Specify a type function for the answer. Will re-prompt the user if the function raises any errors. Common choices here include int, float, and decimal.Decimal. - - validate - This is an optional function that can be used to validate the answer. It should return True or False and have the following signature: - - def function_name(answer, *args, **kwargs): - """ - if not args and kwargs: - args = kwargs - - if default is not None: - prompt = '%s [%s] ' % (prompt, default) - - while True: - cli.echo('') - answer = input(format_ansi(prompt % args)) - cli.echo('') - - if answer: - if validate is not None and not validate(answer, *args, **kwargs): - continue - - elif confirm: - if yesno('Is the answer "%s" correct?', answer, default=True): - try: - return answer_type(answer) - except Exception as e: - cli.log.error('Could not convert answer (%s) to type %s: %s', answer, answer_type.__name__, str(e)) - - else: - try: - return answer_type(answer) - except Exception as e: - cli.log.error('Could not convert answer (%s) to type %s: %s', answer, answer_type.__name__, str(e)) - - elif default is not None: - return default - - -def choice(heading, options, *args, default=None, confirm=False, prompt='Please enter your choice: ', **kwargs): - """Present the user with a list of options and let them pick one. - - Users can enter either the number or the text of their choice. - - This will return the value of the item they choose, not the numerical index. - - Arguments: - heading - The text to place above the list of options. - - options - A sequence of items to choose from. - - default - The index of the item to return when the user doesn't enter any value. Use None to prompt until they enter a value. - - confirm - Present the user with a confirmation dialog before accepting their answer. - - prompt - The prompt to present to the user. Can include ANSI and format strings like milc's `cli.echo()`. - """ - if not args and kwargs: - args = kwargs - - if prompt and default: - prompt = prompt + ' [%s] ' % (default + 1,) - - while True: - # Prompt for an answer. - cli.echo('') - cli.echo(heading % args) - cli.echo('') - for i, option in enumerate(options, 1): - cli.echo('\t{fg_cyan}%d.{fg_reset} %s', i, option) - - cli.echo('') - answer = input(format_ansi(prompt)) - cli.echo('') - - # If the user types in one of the options exactly use that - if answer in options: - return answer - - # Massage the answer into a valid integer - if answer == '' and default: - answer = default - else: - try: - answer = int(answer) - 1 - except Exception: - # Normally we would log the exception here, but in the interest of clean UI we do not. - cli.log.error('Invalid choice: %s', answer + 1) - continue - - # Validate the answer - if answer >= len(options) or answer < 0: - cli.log.error('Invalid choice: %s', answer + 1) - continue - - if confirm and not yesno('Is the answer "%s" correct?', answer + 1, default=True): - continue - - # Return the answer they chose. - return options[answer] diff --git a/message.mk b/message.mk index be04fa9b8104..84b23c1ba871 100644 --- a/message.mk +++ b/message.mk @@ -5,6 +5,7 @@ ifeq ($(COLOR),true) OK_COLOR=\033[32;01m ERROR_COLOR=\033[31;01m WARN_COLOR=\033[33;01m + SKIPPED_COLOR=\033[36;01m BLUE=\033[0;34m BOLD=\033[1m endif @@ -20,6 +21,7 @@ ON_ERROR ?= exit 1 OK_STRING=$(OK_COLOR)[OK]$(NO_COLOR)\n ERROR_STRING=$(ERROR_COLOR)[ERRORS]$(NO_COLOR)\n WARN_STRING=$(WARN_COLOR)[WARNINGS]$(NO_COLOR)\n +SKIPPED_STRING=$(SKIPPED_COLOR)[SKIPPED]$(NO_COLOR)\n TAB_LOG = printf "\n%s\n\n" "$$LOG" | $(AWK) '{ sub(/^/," | "); print }' TAB_LOG_PLAIN = printf "%s\n" "$$LOG" @@ -29,6 +31,7 @@ PRINT_ERROR = ($(SILENT) ||printf " $(ERROR_STRING)" | $(AWK_STATUS)) && $(TAB_L PRINT_WARNING = ($(SILENT) || printf " $(WARN_STRING)" | $(AWK_STATUS)) && $(TAB_LOG) PRINT_ERROR_PLAIN = ($(SILENT) ||printf " $(ERROR_STRING)" | $(AWK_STATUS)) && $(TAB_LOG_PLAIN) && $(ON_ERROR) PRINT_WARNING_PLAIN = ($(SILENT) || printf " $(WARN_STRING)" | $(AWK_STATUS)) && $(TAB_LOG_PLAIN) +PRINT_SKIPPED_PLAIN = ($(SILENT) || printf " $(SKIPPED_STRING)" | $(AWK_STATUS)) PRINT_OK = $(SILENT) || printf " $(OK_STRING)" | $(AWK_STATUS) BUILD_CMD = LOG=$$($(CMD) 2>&1) ; if [ $$? -gt 0 ]; then $(PRINT_ERROR); elif [ "$$LOG" != "" ] ; then $(PRINT_WARNING); else $(PRINT_OK); fi; MAKE_MSG_FORMAT = $(AWK) '{ printf "%-118s", $$0;}' diff --git a/platforms/chibios/BLACKPILL_STM32_F401/configs/config.h b/platforms/chibios/BLACKPILL_STM32_F401/configs/config.h index eb73e72ef282..1c9d9c61263f 100644 --- a/platforms/chibios/BLACKPILL_STM32_F401/configs/config.h +++ b/platforms/chibios/BLACKPILL_STM32_F401/configs/config.h @@ -20,4 +20,6 @@ #define STM32_LSECLK 32768U #define STM32_HSECLK 25000000U -#define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE +#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP +# define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE +#endif diff --git a/platforms/chibios/BLACKPILL_STM32_F411/configs/config.h b/platforms/chibios/BLACKPILL_STM32_F411/configs/config.h index eb73e72ef282..1c9d9c61263f 100644 --- a/platforms/chibios/BLACKPILL_STM32_F411/configs/config.h +++ b/platforms/chibios/BLACKPILL_STM32_F411/configs/config.h @@ -20,4 +20,6 @@ #define STM32_LSECLK 32768U #define STM32_HSECLK 25000000U -#define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE +#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP +# define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE +#endif diff --git a/platforms/chibios/GENERIC_STM32_F042X6/configs/mcuconf.h b/platforms/chibios/GENERIC_STM32_F042X6/configs/mcuconf.h new file mode 100644 index 000000000000..4643e9f92e8f --- /dev/null +++ b/platforms/chibios/GENERIC_STM32_F042X6/configs/mcuconf.h @@ -0,0 +1,168 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef _MCUCONF_H_ +#define _MCUCONF_H_ + +/* + * STM32F0xx drivers configuration. + * The following settings override the default settings present in + * the various device driver implementation headers. + * Note that the settings for each driver only have effect if the whole + * driver is enabled in halconf.h. + * + * IRQ priorities: + * 3...0 Lowest...Highest. + * + * DMA priorities: + * 0...3 Lowest...Highest. + */ + +#define STM32F0xx_MCUCONF + +/* + * HAL driver system settings. + */ +#define STM32_NO_INIT FALSE +#define STM32_PVD_ENABLE FALSE +#define STM32_PLS STM32_PLS_LEV0 +#define STM32_HSI_ENABLED TRUE +#define STM32_HSI14_ENABLED TRUE +#define STM32_HSI48_ENABLED FALSE +#define STM32_LSI_ENABLED TRUE +#define STM32_HSE_ENABLED FALSE +#define STM32_LSE_ENABLED FALSE +#define STM32_SW STM32_SW_PLL +#define STM32_PLLSRC STM32_PLLSRC_HSI_DIV2 +#define STM32_PREDIV_VALUE 1 +#define STM32_PLLMUL_VALUE 12 +#define STM32_HPRE STM32_HPRE_DIV1 +#define STM32_PPRE STM32_PPRE_DIV1 +#define STM32_ADCSW STM32_ADCSW_HSI14 +#define STM32_ADCPRE STM32_ADCPRE_DIV4 +#define STM32_MCOSEL STM32_MCOSEL_NOCLOCK +#define STM32_ADCPRE STM32_ADCPRE_DIV4 +#define STM32_ADCSW STM32_ADCSW_HSI14 +#define STM32_USBSW STM32_USBSW_HSI48 +#define STM32_CECSW STM32_CECSW_HSI +#define STM32_I2C1SW STM32_I2C1SW_HSI +#define STM32_USART1SW STM32_USART1SW_PCLK +#define STM32_RTCSEL STM32_RTCSEL_LSI + +/* + * ADC driver system settings. + */ +#define STM32_ADC_USE_ADC1 FALSE +#define STM32_ADC_ADC1_DMA_PRIORITY 2 +#define STM32_ADC_IRQ_PRIORITY 2 +#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY 2 + +/* + * EXT driver system settings. + */ +#define STM32_EXT_EXTI0_1_IRQ_PRIORITY 3 +#define STM32_EXT_EXTI2_3_IRQ_PRIORITY 3 +#define STM32_EXT_EXTI4_15_IRQ_PRIORITY 3 +#define STM32_EXT_EXTI16_IRQ_PRIORITY 3 +#define STM32_EXT_EXTI17_IRQ_PRIORITY 3 + +/* + * GPT driver system settings. + */ +#define STM32_GPT_USE_TIM1 FALSE +#define STM32_GPT_USE_TIM2 FALSE +#define STM32_GPT_USE_TIM3 FALSE +#define STM32_GPT_USE_TIM14 FALSE +#define STM32_GPT_TIM1_IRQ_PRIORITY 2 +#define STM32_GPT_TIM2_IRQ_PRIORITY 2 +#define STM32_GPT_TIM3_IRQ_PRIORITY 2 +#define STM32_GPT_TIM14_IRQ_PRIORITY 2 + +/* + * I2C driver system settings. + */ +#define STM32_I2C_USE_I2C1 FALSE +#define STM32_I2C_USE_I2C2 FALSE +#define STM32_I2C_BUSY_TIMEOUT 50 +#define STM32_I2C_I2C1_IRQ_PRIORITY 3 +#define STM32_I2C_I2C2_IRQ_PRIORITY 3 +#define STM32_I2C_USE_DMA TRUE +#define STM32_I2C_I2C1_DMA_PRIORITY 1 +#define STM32_I2C_I2C2_DMA_PRIORITY 1 +#define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure") + +/* + * ICU driver system settings. + */ +#define STM32_ICU_USE_TIM1 FALSE +#define STM32_ICU_USE_TIM2 FALSE +#define STM32_ICU_USE_TIM3 FALSE +#define STM32_ICU_TIM1_IRQ_PRIORITY 3 +#define STM32_ICU_TIM2_IRQ_PRIORITY 3 +#define STM32_ICU_TIM3_IRQ_PRIORITY 3 + +/* + * PWM driver system settings. + */ +#define STM32_PWM_USE_ADVANCED FALSE +#define STM32_PWM_USE_TIM1 FALSE +#define STM32_PWM_USE_TIM2 FALSE +#define STM32_PWM_USE_TIM3 FALSE +#define STM32_PWM_TIM1_IRQ_PRIORITY 3 +#define STM32_PWM_TIM2_IRQ_PRIORITY 3 +#define STM32_PWM_TIM3_IRQ_PRIORITY 3 + +/* + * SERIAL driver system settings. + */ +#define STM32_SERIAL_USE_USART1 FALSE +#define STM32_SERIAL_USE_USART2 FALSE +#define STM32_SERIAL_USART1_PRIORITY 3 +#define STM32_SERIAL_USART2_PRIORITY 3 + +/* + * SPI driver system settings. + */ +#define STM32_SPI_USE_SPI1 FALSE +#define STM32_SPI_USE_SPI2 FALSE +#define STM32_SPI_SPI1_DMA_PRIORITY 1 +#define STM32_SPI_SPI2_DMA_PRIORITY 1 +#define STM32_SPI_SPI1_IRQ_PRIORITY 2 +#define STM32_SPI_SPI2_IRQ_PRIORITY 2 +#define STM32_SPI_DMA_ERROR_HOOK(spip) osalSysHalt("DMA failure") + +/* + * ST driver system settings. + */ +#define STM32_ST_IRQ_PRIORITY 2 +#define STM32_ST_USE_TIMER 2 + +/* + * UART driver system settings. + */ +#define STM32_UART_USE_USART1 FALSE +#define STM32_UART_USE_USART2 FALSE +#define STM32_UART_USART1_IRQ_PRIORITY 3 +#define STM32_UART_USART2_IRQ_PRIORITY 3 +#define STM32_UART_USART1_DMA_PRIORITY 0 +#define STM32_UART_USART2_DMA_PRIORITY 0 +#define STM32_UART_DMA_ERROR_HOOK(uartp) osalSysHalt("DMA failure") + +/* + * USB driver system settings. + */ +#define STM32_USB_USE_USB1 TRUE +#define STM32_USB_LOW_POWER_ON_SUSPEND FALSE +#define STM32_USB_USB1_LP_IRQ_PRIORITY 3 + +#endif /* _MCUCONF_H_ */ \ No newline at end of file diff --git a/platforms/chibios/GENERIC_STM32_F072XB/board/board.c b/platforms/chibios/GENERIC_STM32_F072XB/board/board.c deleted file mode 100644 index c91136e8f664..000000000000 --- a/platforms/chibios/GENERIC_STM32_F072XB/board/board.c +++ /dev/null @@ -1,250 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/* - * This file has been automatically generated using ChibiStudio board - * generator plugin. Do not edit manually. - */ - -#include "hal.h" -#include "stm32_gpio.h" - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/** - * @brief Type of STM32 GPIO port setup. - */ -typedef struct { - uint32_t moder; - uint32_t otyper; - uint32_t ospeedr; - uint32_t pupdr; - uint32_t odr; - uint32_t afrl; - uint32_t afrh; -} gpio_setup_t; - -/** - * @brief Type of STM32 GPIO initialization data. - */ -typedef struct { -#if STM32_HAS_GPIOA || defined(__DOXYGEN__) - gpio_setup_t PAData; -#endif -#if STM32_HAS_GPIOB || defined(__DOXYGEN__) - gpio_setup_t PBData; -#endif -#if STM32_HAS_GPIOC || defined(__DOXYGEN__) - gpio_setup_t PCData; -#endif -#if STM32_HAS_GPIOD || defined(__DOXYGEN__) - gpio_setup_t PDData; -#endif -#if STM32_HAS_GPIOE || defined(__DOXYGEN__) - gpio_setup_t PEData; -#endif -#if STM32_HAS_GPIOF || defined(__DOXYGEN__) - gpio_setup_t PFData; -#endif -#if STM32_HAS_GPIOG || defined(__DOXYGEN__) - gpio_setup_t PGData; -#endif -#if STM32_HAS_GPIOH || defined(__DOXYGEN__) - gpio_setup_t PHData; -#endif -#if STM32_HAS_GPIOI || defined(__DOXYGEN__) - gpio_setup_t PIData; -#endif -#if STM32_HAS_GPIOJ || defined(__DOXYGEN__) - gpio_setup_t PJData; -#endif -#if STM32_HAS_GPIOK || defined(__DOXYGEN__) - gpio_setup_t PKData; -#endif -} gpio_config_t; - -/** - * @brief STM32 GPIO static initialization data. - */ -static const gpio_config_t gpio_default_config = { -#if STM32_HAS_GPIOA - {VAL_GPIOA_MODER, VAL_GPIOA_OTYPER, VAL_GPIOA_OSPEEDR, VAL_GPIOA_PUPDR, VAL_GPIOA_ODR, VAL_GPIOA_AFRL, VAL_GPIOA_AFRH}, -#endif -#if STM32_HAS_GPIOB - {VAL_GPIOB_MODER, VAL_GPIOB_OTYPER, VAL_GPIOB_OSPEEDR, VAL_GPIOB_PUPDR, VAL_GPIOB_ODR, VAL_GPIOB_AFRL, VAL_GPIOB_AFRH}, -#endif -#if STM32_HAS_GPIOC - {VAL_GPIOC_MODER, VAL_GPIOC_OTYPER, VAL_GPIOC_OSPEEDR, VAL_GPIOC_PUPDR, VAL_GPIOC_ODR, VAL_GPIOC_AFRL, VAL_GPIOC_AFRH}, -#endif -#if STM32_HAS_GPIOD - {VAL_GPIOD_MODER, VAL_GPIOD_OTYPER, VAL_GPIOD_OSPEEDR, VAL_GPIOD_PUPDR, VAL_GPIOD_ODR, VAL_GPIOD_AFRL, VAL_GPIOD_AFRH}, -#endif -#if STM32_HAS_GPIOE - {VAL_GPIOE_MODER, VAL_GPIOE_OTYPER, VAL_GPIOE_OSPEEDR, VAL_GPIOE_PUPDR, VAL_GPIOE_ODR, VAL_GPIOE_AFRL, VAL_GPIOE_AFRH}, -#endif -#if STM32_HAS_GPIOF - {VAL_GPIOF_MODER, VAL_GPIOF_OTYPER, VAL_GPIOF_OSPEEDR, VAL_GPIOF_PUPDR, VAL_GPIOF_ODR, VAL_GPIOF_AFRL, VAL_GPIOF_AFRH}, -#endif -#if STM32_HAS_GPIOG - {VAL_GPIOG_MODER, VAL_GPIOG_OTYPER, VAL_GPIOG_OSPEEDR, VAL_GPIOG_PUPDR, VAL_GPIOG_ODR, VAL_GPIOG_AFRL, VAL_GPIOG_AFRH}, -#endif -#if STM32_HAS_GPIOH - {VAL_GPIOH_MODER, VAL_GPIOH_OTYPER, VAL_GPIOH_OSPEEDR, VAL_GPIOH_PUPDR, VAL_GPIOH_ODR, VAL_GPIOH_AFRL, VAL_GPIOH_AFRH}, -#endif -#if STM32_HAS_GPIOI - {VAL_GPIOI_MODER, VAL_GPIOI_OTYPER, VAL_GPIOI_OSPEEDR, VAL_GPIOI_PUPDR, VAL_GPIOI_ODR, VAL_GPIOI_AFRL, VAL_GPIOI_AFRH}, -#endif -#if STM32_HAS_GPIOJ - {VAL_GPIOJ_MODER, VAL_GPIOJ_OTYPER, VAL_GPIOJ_OSPEEDR, VAL_GPIOJ_PUPDR, VAL_GPIOJ_ODR, VAL_GPIOJ_AFRL, VAL_GPIOJ_AFRH}, -#endif -#if STM32_HAS_GPIOK - {VAL_GPIOK_MODER, VAL_GPIOK_OTYPER, VAL_GPIOK_OSPEEDR, VAL_GPIOK_PUPDR, VAL_GPIOK_ODR, VAL_GPIOK_AFRL, VAL_GPIOK_AFRH} -#endif -}; - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -static void gpio_init(stm32_gpio_t *gpiop, const gpio_setup_t *config) { - gpiop->OTYPER = config->otyper; - gpiop->OSPEEDR = config->ospeedr; - gpiop->PUPDR = config->pupdr; - gpiop->ODR = config->odr; - gpiop->AFRL = config->afrl; - gpiop->AFRH = config->afrh; - gpiop->MODER = config->moder; -} - -static void stm32_gpio_init(void) { - /* Enabling GPIO-related clocks, the mask comes from the - registry header file.*/ - rccResetAHB(STM32_GPIO_EN_MASK); - rccEnableAHB(STM32_GPIO_EN_MASK, true); - - /* Initializing all the defined GPIO ports.*/ -#if STM32_HAS_GPIOA - gpio_init(GPIOA, &gpio_default_config.PAData); -#endif -#if STM32_HAS_GPIOB - gpio_init(GPIOB, &gpio_default_config.PBData); -#endif -#if STM32_HAS_GPIOC - gpio_init(GPIOC, &gpio_default_config.PCData); -#endif -#if STM32_HAS_GPIOD - gpio_init(GPIOD, &gpio_default_config.PDData); -#endif -#if STM32_HAS_GPIOE - gpio_init(GPIOE, &gpio_default_config.PEData); -#endif -#if STM32_HAS_GPIOF - gpio_init(GPIOF, &gpio_default_config.PFData); -#endif -#if STM32_HAS_GPIOG - gpio_init(GPIOG, &gpio_default_config.PGData); -#endif -#if STM32_HAS_GPIOH - gpio_init(GPIOH, &gpio_default_config.PHData); -#endif -#if STM32_HAS_GPIOI - gpio_init(GPIOI, &gpio_default_config.PIData); -#endif -#if STM32_HAS_GPIOJ - gpio_init(GPIOJ, &gpio_default_config.PJData); -#endif -#if STM32_HAS_GPIOK - gpio_init(GPIOK, &gpio_default_config.PKData); -#endif -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -__attribute__((weak)) void enter_bootloader_mode_if_requested(void) {} - -/** - * @brief Early initialization code. - * @details GPIO ports and system clocks are initialized before everything - * else. - */ -void __early_init(void) { - enter_bootloader_mode_if_requested(); - - stm32_gpio_init(); - stm32_clock_init(); -} - -#if HAL_USE_SDC || defined(__DOXYGEN__) -/** - * @brief SDC card detection. - */ -bool sdc_lld_is_card_inserted(SDCDriver *sdcp) { - (void)sdcp; - /* TODO: Fill the implementation.*/ - return true; -} - -/** - * @brief SDC card write protection detection. - */ -bool sdc_lld_is_write_protected(SDCDriver *sdcp) { - (void)sdcp; - /* TODO: Fill the implementation.*/ - return false; -} -#endif /* HAL_USE_SDC */ - -#if HAL_USE_MMC_SPI || defined(__DOXYGEN__) -/** - * @brief MMC_SPI card detection. - */ -bool mmc_lld_is_card_inserted(MMCDriver *mmcp) { - (void)mmcp; - /* TODO: Fill the implementation.*/ - return true; -} - -/** - * @brief MMC_SPI card write protection detection. - */ -bool mmc_lld_is_write_protected(MMCDriver *mmcp) { - (void)mmcp; - /* TODO: Fill the implementation.*/ - return false; -} -#endif - -/** - * @brief Board-specific initialization code. - * @todo Add your board-specific code, if any. - */ -void boardInit(void) {} diff --git a/platforms/chibios/GENERIC_STM32_F072XB/board/board.h b/platforms/chibios/GENERIC_STM32_F072XB/board/board.h deleted file mode 100644 index 87570e62d2ab..000000000000 --- a/platforms/chibios/GENERIC_STM32_F072XB/board/board.h +++ /dev/null @@ -1,407 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/* - * This file has been automatically generated using ChibiStudio board - * generator plugin. Do not edit manually. - */ - -#ifndef BOARD_H -#define BOARD_H - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/* - * Setup for Generic STM32_F072 Board - */ - -/* - * Board identifier. - */ -#define BOARD_GENERIC_STM32_F072XB -#define BOARD_NAME "STM32_F072" - -/* - * Board oscillators-related settings. - * NOTE: LSE not fitted. - * NOTE: HSE not fitted. - */ -#if !defined(STM32_LSECLK) -# define STM32_LSECLK 0U -#endif - -#define STM32_LSEDRV (3U << 3U) - -#if !defined(STM32_HSECLK) -# define STM32_HSECLK 0U -#endif - -#define STM32_HSE_BYPASS - -/* - * MCU type as defined in the ST header. - */ -#define STM32F072xB - -/* - * IO pins assignments. - */ -#define GPIOA_BUTTON 0U -#define GPIOA_PIN1 1U -#define GPIOA_PIN2 2U -#define GPIOA_PIN3 3U -#define GPIOA_PIN4 4U -#define GPIOA_PIN5 5U -#define GPIOA_PIN6 6U -#define GPIOA_PIN7 7U -#define GPIOA_PIN8 8U -#define GPIOA_PIN9 9U -#define GPIOA_PIN10 10U -#define GPIOA_USB_DM 11U -#define GPIOA_USB_DP 12U -#define GPIOA_SWDIO 13U -#define GPIOA_SWCLK 14U -#define GPIOA_PIN15 15U - -#define GPIOB_PIN0 0U -#define GPIOB_PIN1 1U -#define GPIOB_PIN2 2U -#define GPIOB_PIN3 3U -#define GPIOB_PIN4 4U -#define GPIOB_PIN5 5U -#define GPIOB_PIN6 6U -#define GPIOB_PIN7 7U -#define GPIOB_PIN8 8U -#define GPIOB_PIN9 9U -#define GPIOB_PIN10 10U -#define GPIOB_PIN11 11U -#define GPIOB_PIN12 12U -#define GPIOB_SPI2_SCK 13U -#define GPIOB_SPI2_MISO 14U -#define GPIOB_SPI2_MOSI 15U - -#define GPIOC_MEMS_CS 0U -#define GPIOC_PIN1 1U -#define GPIOC_PIN2 2U -#define GPIOC_PIN3 3U -#define GPIOC_PIN4 4U -#define GPIOC_PIN5 5U -#define GPIOC_LED_RED 6U -#define GPIOC_LED_BLUE 7U -#define GPIOC_LED_ORANGE 8U -#define GPIOC_LED_GREEN 9U -#define GPIOC_PIN10 10U -#define GPIOC_PIN11 11U -#define GPIOC_PIN12 12U -#define GPIOC_PIN13 13U -#define GPIOC_OSC32_IN 14U -#define GPIOC_OSC32_OUT 15U - -#define GPIOD_PIN0 0U -#define GPIOD_PIN1 1U -#define GPIOD_PIN2 2U -#define GPIOD_PIN3 3U -#define GPIOD_PIN4 4U -#define GPIOD_PIN5 5U -#define GPIOD_PIN6 6U -#define GPIOD_PIN7 7U -#define GPIOD_PIN8 8U -#define GPIOD_PIN9 9U -#define GPIOD_PIN10 10U -#define GPIOD_PIN11 11U -#define GPIOD_PIN12 12U -#define GPIOD_PIN13 13U -#define GPIOD_PIN14 14U -#define GPIOD_PIN15 15U - -#define GPIOE_PIN0 0U -#define GPIOE_PIN1 1U -#define GPIOE_PIN2 2U -#define GPIOE_PIN3 3U -#define GPIOE_PIN4 4U -#define GPIOE_PIN5 5U -#define GPIOE_PIN6 6U -#define GPIOE_PIN7 7U -#define GPIOE_PIN8 8U -#define GPIOE_PIN9 9U -#define GPIOE_PIN10 10U -#define GPIOE_PIN11 11U -#define GPIOE_PIN12 12U -#define GPIOE_PIN13 13U -#define GPIOE_PIN14 14U -#define GPIOE_PIN15 15U - -#define GPIOF_OSC_IN 0U -#define GPIOF_OSC_OUT 1U -#define GPIOF_PIN2 2U -#define GPIOF_PIN3 3U -#define GPIOF_PIN4 4U -#define GPIOF_PIN5 5U -#define GPIOF_PIN6 6U -#define GPIOF_PIN7 7U -#define GPIOF_PIN8 8U -#define GPIOF_PIN9 9U -#define GPIOF_PIN10 10U -#define GPIOF_PIN11 11U -#define GPIOF_PIN12 12U -#define GPIOF_PIN13 13U -#define GPIOF_PIN14 14U -#define GPIOF_PIN15 15U - -/* - * IO lines assignments. - */ -#define LINE_BUTTON PAL_LINE(GPIOA, 0U) -#define LINE_USB_DM PAL_LINE(GPIOA, 11U) -#define LINE_USB_DP PAL_LINE(GPIOA, 12U) -#define LINE_SWDIO PAL_LINE(GPIOA, 13U) -#define LINE_SWCLK PAL_LINE(GPIOA, 14U) -#define LINE_SPI2_SCK PAL_LINE(GPIOB, 13U) -#define LINE_SPI2_MISO PAL_LINE(GPIOB, 14U) -#define LINE_SPI2_MOSI PAL_LINE(GPIOB, 15U) -#define LINE_MEMS_CS PAL_LINE(GPIOC, 0U) -#define LINE_LED_RED PAL_LINE(GPIOC, 6U) -#define LINE_LED_BLUE PAL_LINE(GPIOC, 7U) -#define LINE_LED_ORANGE PAL_LINE(GPIOC, 8U) -#define LINE_LED_GREEN PAL_LINE(GPIOC, 9U) -#define LINE_OSC32_IN PAL_LINE(GPIOC, 14U) -#define LINE_OSC32_OUT PAL_LINE(GPIOC, 15U) -#define LINE_OSC_IN PAL_LINE(GPIOF, 0U) -#define LINE_OSC_OUT PAL_LINE(GPIOF, 1U) - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/* - * I/O ports initial setup, this configuration is established soon after reset - * in the initialization code. - * Please refer to the STM32 Reference Manual for details. - */ -#define PIN_MODE_INPUT(n) (0U << ((n)*2U)) -#define PIN_MODE_OUTPUT(n) (1U << ((n)*2U)) -#define PIN_MODE_ALTERNATE(n) (2U << ((n)*2U)) -#define PIN_MODE_ANALOG(n) (3U << ((n)*2U)) -#define PIN_ODR_LOW(n) (0U << (n)) -#define PIN_ODR_HIGH(n) (1U << (n)) -#define PIN_OTYPE_PUSHPULL(n) (0U << (n)) -#define PIN_OTYPE_OPENDRAIN(n) (1U << (n)) -#define PIN_OSPEED_VERYLOW(n) (0U << ((n)*2U)) -#define PIN_OSPEED_LOW(n) (1U << ((n)*2U)) -#define PIN_OSPEED_MEDIUM(n) (2U << ((n)*2U)) -#define PIN_OSPEED_HIGH(n) (3U << ((n)*2U)) -#define PIN_PUPDR_FLOATING(n) (0U << ((n)*2U)) -#define PIN_PUPDR_PULLUP(n) (1U << ((n)*2U)) -#define PIN_PUPDR_PULLDOWN(n) (2U << ((n)*2U)) -#define PIN_AFIO_AF(n, v) ((v) << (((n) % 8U) * 4U)) - -/* - * GPIOA setup: - * - * PA0 - BUTTON (input floating). - * PA1 - PIN1 (input pullup). - * PA2 - PIN2 (input pullup). - * PA3 - PIN3 (input pullup). - * PA4 - PIN4 (input pullup). - * PA5 - PIN5 (input pullup). - * PA6 - PIN6 (input pullup). - * PA7 - PIN7 (input pullup). - * PA8 - PIN8 (input pullup). - * PA9 - PIN9 (input pullup). - * PA10 - PIN10 (input pullup). - * PA11 - USB_DM (input floating). - * PA12 - USB_DP (input floating). - * PA13 - SWDIO (alternate 0). - * PA14 - SWCLK (alternate 0). - * PA15 - PIN15 (input pullup). - */ -#define VAL_GPIOA_MODER (PIN_MODE_INPUT(GPIOA_BUTTON) | PIN_MODE_INPUT(GPIOA_PIN1) | PIN_MODE_INPUT(GPIOA_PIN2) | PIN_MODE_INPUT(GPIOA_PIN3) | PIN_MODE_INPUT(GPIOA_PIN4) | PIN_MODE_INPUT(GPIOA_PIN5) | PIN_MODE_INPUT(GPIOA_PIN6) | PIN_MODE_INPUT(GPIOA_PIN7) | PIN_MODE_INPUT(GPIOA_PIN8) | PIN_MODE_INPUT(GPIOA_PIN9) | PIN_MODE_INPUT(GPIOA_PIN10) | PIN_MODE_INPUT(GPIOA_USB_DM) | PIN_MODE_INPUT(GPIOA_USB_DP) | PIN_MODE_ALTERNATE(GPIOA_SWDIO) | PIN_MODE_ALTERNATE(GPIOA_SWCLK) | PIN_MODE_INPUT(GPIOA_PIN15)) -#define VAL_GPIOA_OTYPER (PIN_OTYPE_PUSHPULL(GPIOA_BUTTON) | PIN_OTYPE_PUSHPULL(GPIOA_PIN1) | PIN_OTYPE_PUSHPULL(GPIOA_PIN2) | PIN_OTYPE_PUSHPULL(GPIOA_PIN3) | PIN_OTYPE_PUSHPULL(GPIOA_PIN4) | PIN_OTYPE_PUSHPULL(GPIOA_PIN5) | PIN_OTYPE_PUSHPULL(GPIOA_PIN6) | PIN_OTYPE_PUSHPULL(GPIOA_PIN7) | PIN_OTYPE_PUSHPULL(GPIOA_PIN8) | PIN_OTYPE_PUSHPULL(GPIOA_PIN9) | PIN_OTYPE_PUSHPULL(GPIOA_PIN10) | PIN_OTYPE_PUSHPULL(GPIOA_USB_DM) | PIN_OTYPE_PUSHPULL(GPIOA_USB_DP) | PIN_OTYPE_PUSHPULL(GPIOA_SWDIO) | PIN_OTYPE_PUSHPULL(GPIOA_SWCLK) | PIN_OTYPE_PUSHPULL(GPIOA_PIN15)) -#define VAL_GPIOA_OSPEEDR (PIN_OSPEED_VERYLOW(GPIOA_BUTTON) | PIN_OSPEED_VERYLOW(GPIOA_PIN1) | PIN_OSPEED_VERYLOW(GPIOA_PIN2) | PIN_OSPEED_VERYLOW(GPIOA_PIN3) | PIN_OSPEED_VERYLOW(GPIOA_PIN4) | PIN_OSPEED_VERYLOW(GPIOA_PIN5) | PIN_OSPEED_VERYLOW(GPIOA_PIN6) | PIN_OSPEED_VERYLOW(GPIOA_PIN7) | PIN_OSPEED_VERYLOW(GPIOA_PIN8) | PIN_OSPEED_VERYLOW(GPIOA_PIN9) | PIN_OSPEED_VERYLOW(GPIOA_PIN10) | PIN_OSPEED_VERYLOW(GPIOA_USB_DM) | PIN_OSPEED_VERYLOW(GPIOA_USB_DP) | PIN_OSPEED_HIGH(GPIOA_SWDIO) | PIN_OSPEED_HIGH(GPIOA_SWCLK) | PIN_OSPEED_HIGH(GPIOA_PIN15)) -#define VAL_GPIOA_PUPDR (PIN_PUPDR_FLOATING(GPIOA_BUTTON) | PIN_PUPDR_PULLUP(GPIOA_PIN1) | PIN_PUPDR_PULLUP(GPIOA_PIN2) | PIN_PUPDR_PULLUP(GPIOA_PIN3) | PIN_PUPDR_PULLUP(GPIOA_PIN4) | PIN_PUPDR_PULLUP(GPIOA_PIN5) | PIN_PUPDR_PULLUP(GPIOA_PIN6) | PIN_PUPDR_PULLUP(GPIOA_PIN7) | PIN_PUPDR_PULLUP(GPIOA_PIN8) | PIN_PUPDR_PULLUP(GPIOA_PIN9) | PIN_PUPDR_PULLUP(GPIOA_PIN10) | PIN_PUPDR_FLOATING(GPIOA_USB_DM) | PIN_PUPDR_FLOATING(GPIOA_USB_DP) | PIN_PUPDR_PULLUP(GPIOA_SWDIO) | PIN_PUPDR_PULLDOWN(GPIOA_SWCLK) | PIN_PUPDR_PULLUP(GPIOA_PIN15)) -#define VAL_GPIOA_ODR (PIN_ODR_HIGH(GPIOA_BUTTON) | PIN_ODR_HIGH(GPIOA_PIN1) | PIN_ODR_HIGH(GPIOA_PIN2) | PIN_ODR_HIGH(GPIOA_PIN3) | PIN_ODR_HIGH(GPIOA_PIN4) | PIN_ODR_HIGH(GPIOA_PIN5) | PIN_ODR_HIGH(GPIOA_PIN6) | PIN_ODR_HIGH(GPIOA_PIN7) | PIN_ODR_HIGH(GPIOA_PIN8) | PIN_ODR_HIGH(GPIOA_PIN9) | PIN_ODR_HIGH(GPIOA_PIN10) | PIN_ODR_HIGH(GPIOA_USB_DM) | PIN_ODR_HIGH(GPIOA_USB_DP) | PIN_ODR_HIGH(GPIOA_SWDIO) | PIN_ODR_HIGH(GPIOA_SWCLK) | PIN_ODR_HIGH(GPIOA_PIN15)) -#define VAL_GPIOA_AFRL (PIN_AFIO_AF(GPIOA_BUTTON, 0U) | PIN_AFIO_AF(GPIOA_PIN1, 0U) | PIN_AFIO_AF(GPIOA_PIN2, 0U) | PIN_AFIO_AF(GPIOA_PIN3, 0U) | PIN_AFIO_AF(GPIOA_PIN4, 0U) | PIN_AFIO_AF(GPIOA_PIN5, 0U) | PIN_AFIO_AF(GPIOA_PIN6, 0U) | PIN_AFIO_AF(GPIOA_PIN7, 0U)) -#define VAL_GPIOA_AFRH (PIN_AFIO_AF(GPIOA_PIN8, 0U) | PIN_AFIO_AF(GPIOA_PIN9, 0U) | PIN_AFIO_AF(GPIOA_PIN10, 0U) | PIN_AFIO_AF(GPIOA_USB_DM, 0U) | PIN_AFIO_AF(GPIOA_USB_DP, 0U) | PIN_AFIO_AF(GPIOA_SWDIO, 0U) | PIN_AFIO_AF(GPIOA_SWCLK, 0U) | PIN_AFIO_AF(GPIOA_PIN15, 0U)) - -/* - * GPIOB setup: - * - * PB0 - PIN0 (input pullup). - * PB1 - PIN1 (input pullup). - * PB2 - PIN2 (input pullup). - * PB3 - PIN3 (input pullup). - * PB4 - PIN4 (input pullup). - * PB5 - PIN5 (input pullup). - * PB6 - PIN6 (input pullup). - * PB7 - PIN7 (input pullup). - * PB8 - PIN8 (input pullup). - * PB9 - PIN9 (input pullup). - * PB10 - PIN10 (input pullup). - * PB11 - PIN11 (input pullup). - * PB12 - PIN12 (input pullup). - * PB13 - SPI2_SCK (alternate 0). - * PB14 - SPI2_MISO (alternate 0). - * PB15 - SPI2_MOSI (alternate 0). - */ -#define VAL_GPIOB_MODER (PIN_MODE_INPUT(GPIOB_PIN0) | PIN_MODE_INPUT(GPIOB_PIN1) | PIN_MODE_INPUT(GPIOB_PIN2) | PIN_MODE_INPUT(GPIOB_PIN3) | PIN_MODE_INPUT(GPIOB_PIN4) | PIN_MODE_INPUT(GPIOB_PIN5) | PIN_MODE_INPUT(GPIOB_PIN6) | PIN_MODE_INPUT(GPIOB_PIN7) | PIN_MODE_INPUT(GPIOB_PIN8) | PIN_MODE_INPUT(GPIOB_PIN9) | PIN_MODE_INPUT(GPIOB_PIN10) | PIN_MODE_INPUT(GPIOB_PIN11) | PIN_MODE_INPUT(GPIOB_PIN12) | PIN_MODE_ALTERNATE(GPIOB_SPI2_SCK) | PIN_MODE_ALTERNATE(GPIOB_SPI2_MISO) | PIN_MODE_ALTERNATE(GPIOB_SPI2_MOSI)) -#define VAL_GPIOB_OTYPER (PIN_OTYPE_PUSHPULL(GPIOB_PIN0) | PIN_OTYPE_PUSHPULL(GPIOB_PIN1) | PIN_OTYPE_PUSHPULL(GPIOB_PIN2) | PIN_OTYPE_PUSHPULL(GPIOB_PIN3) | PIN_OTYPE_PUSHPULL(GPIOB_PIN4) | PIN_OTYPE_PUSHPULL(GPIOB_PIN5) | PIN_OTYPE_PUSHPULL(GPIOB_PIN6) | PIN_OTYPE_PUSHPULL(GPIOB_PIN7) | PIN_OTYPE_PUSHPULL(GPIOB_PIN8) | PIN_OTYPE_PUSHPULL(GPIOB_PIN9) | PIN_OTYPE_PUSHPULL(GPIOB_PIN10) | PIN_OTYPE_PUSHPULL(GPIOB_PIN11) | PIN_OTYPE_PUSHPULL(GPIOB_PIN12) | PIN_OTYPE_PUSHPULL(GPIOB_SPI2_SCK) | PIN_OTYPE_PUSHPULL(GPIOB_SPI2_MISO) | PIN_OTYPE_PUSHPULL(GPIOB_SPI2_MOSI)) -#define VAL_GPIOB_OSPEEDR (PIN_OSPEED_VERYLOW(GPIOB_PIN0) | PIN_OSPEED_VERYLOW(GPIOB_PIN1) | PIN_OSPEED_HIGH(GPIOB_PIN2) | PIN_OSPEED_HIGH(GPIOB_PIN3) | PIN_OSPEED_HIGH(GPIOB_PIN4) | PIN_OSPEED_VERYLOW(GPIOB_PIN5) | PIN_OSPEED_VERYLOW(GPIOB_PIN6) | PIN_OSPEED_VERYLOW(GPIOB_PIN7) | PIN_OSPEED_VERYLOW(GPIOB_PIN8) | PIN_OSPEED_VERYLOW(GPIOB_PIN9) | PIN_OSPEED_VERYLOW(GPIOB_PIN10) | PIN_OSPEED_VERYLOW(GPIOB_PIN11) | PIN_OSPEED_VERYLOW(GPIOB_PIN12) | PIN_OSPEED_VERYLOW(GPIOB_SPI2_SCK) | PIN_OSPEED_VERYLOW(GPIOB_SPI2_MISO) | PIN_OSPEED_VERYLOW(GPIOB_SPI2_MOSI)) -#define VAL_GPIOB_PUPDR (PIN_PUPDR_PULLUP(GPIOB_PIN0) | PIN_PUPDR_PULLUP(GPIOB_PIN1) | PIN_PUPDR_PULLUP(GPIOB_PIN2) | PIN_PUPDR_PULLUP(GPIOB_PIN3) | PIN_PUPDR_PULLUP(GPIOB_PIN4) | PIN_PUPDR_PULLUP(GPIOB_PIN5) | PIN_PUPDR_PULLUP(GPIOB_PIN6) | PIN_PUPDR_PULLUP(GPIOB_PIN7) | PIN_PUPDR_PULLUP(GPIOB_PIN8) | PIN_PUPDR_PULLUP(GPIOB_PIN9) | PIN_PUPDR_PULLUP(GPIOB_PIN10) | PIN_PUPDR_PULLUP(GPIOB_PIN11) | PIN_PUPDR_PULLUP(GPIOB_PIN12) | PIN_PUPDR_FLOATING(GPIOB_SPI2_SCK) | PIN_PUPDR_FLOATING(GPIOB_SPI2_MISO) | PIN_PUPDR_FLOATING(GPIOB_SPI2_MOSI)) -#define VAL_GPIOB_ODR (PIN_ODR_HIGH(GPIOB_PIN0) | PIN_ODR_HIGH(GPIOB_PIN1) | PIN_ODR_HIGH(GPIOB_PIN2) | PIN_ODR_HIGH(GPIOB_PIN3) | PIN_ODR_HIGH(GPIOB_PIN4) | PIN_ODR_HIGH(GPIOB_PIN5) | PIN_ODR_HIGH(GPIOB_PIN6) | PIN_ODR_HIGH(GPIOB_PIN7) | PIN_ODR_HIGH(GPIOB_PIN8) | PIN_ODR_HIGH(GPIOB_PIN9) | PIN_ODR_HIGH(GPIOB_PIN10) | PIN_ODR_HIGH(GPIOB_PIN11) | PIN_ODR_HIGH(GPIOB_PIN12) | PIN_ODR_HIGH(GPIOB_SPI2_SCK) | PIN_ODR_HIGH(GPIOB_SPI2_MISO) | PIN_ODR_HIGH(GPIOB_SPI2_MOSI)) -#define VAL_GPIOB_AFRL (PIN_AFIO_AF(GPIOB_PIN0, 0U) | PIN_AFIO_AF(GPIOB_PIN1, 0U) | PIN_AFIO_AF(GPIOB_PIN2, 0U) | PIN_AFIO_AF(GPIOB_PIN3, 0U) | PIN_AFIO_AF(GPIOB_PIN4, 0U) | PIN_AFIO_AF(GPIOB_PIN5, 0U) | PIN_AFIO_AF(GPIOB_PIN6, 0U) | PIN_AFIO_AF(GPIOB_PIN7, 0U)) -#define VAL_GPIOB_AFRH (PIN_AFIO_AF(GPIOB_PIN8, 0U) | PIN_AFIO_AF(GPIOB_PIN9, 0U) | PIN_AFIO_AF(GPIOB_PIN10, 0U) | PIN_AFIO_AF(GPIOB_PIN11, 0U) | PIN_AFIO_AF(GPIOB_PIN12, 0U) | PIN_AFIO_AF(GPIOB_SPI2_SCK, 0U) | PIN_AFIO_AF(GPIOB_SPI2_MISO, 0U) | PIN_AFIO_AF(GPIOB_SPI2_MOSI, 0U)) - -/* - * GPIOC setup: - * - * PC0 - MEMS_CS (output pushpull maximum). - * PC1 - PIN1 (input pullup). - * PC2 - PIN2 (input pullup). - * PC3 - PIN3 (input pullup). - * PC4 - PIN4 (input pullup). - * PC5 - PIN5 (input pullup). - * PC6 - LED_RED (output pushpull maximum). - * PC7 - LED_BLUE (output pushpull maximum). - * PC8 - LED_ORANGE (output pushpull maximum). - * PC9 - LED_GREEN (output pushpull maximum). - * PC10 - PIN10 (input pullup). - * PC11 - PIN11 (input pullup). - * PC12 - PIN12 (input pullup). - * PC13 - PIN13 (input pullup). - * PC14 - OSC32_IN (input floating). - * PC15 - OSC32_OUT (input floating). - */ -#define VAL_GPIOC_MODER (PIN_MODE_OUTPUT(GPIOC_MEMS_CS) | PIN_MODE_INPUT(GPIOC_PIN1) | PIN_MODE_INPUT(GPIOC_PIN2) | PIN_MODE_INPUT(GPIOC_PIN3) | PIN_MODE_INPUT(GPIOC_PIN4) | PIN_MODE_INPUT(GPIOC_PIN5) | PIN_MODE_OUTPUT(GPIOC_LED_RED) | PIN_MODE_OUTPUT(GPIOC_LED_BLUE) | PIN_MODE_OUTPUT(GPIOC_LED_ORANGE) | PIN_MODE_OUTPUT(GPIOC_LED_GREEN) | PIN_MODE_INPUT(GPIOC_PIN10) | PIN_MODE_INPUT(GPIOC_PIN11) | PIN_MODE_INPUT(GPIOC_PIN12) | PIN_MODE_INPUT(GPIOC_PIN13) | PIN_MODE_INPUT(GPIOC_OSC32_IN) | PIN_MODE_INPUT(GPIOC_OSC32_OUT)) -#define VAL_GPIOC_OTYPER (PIN_OTYPE_PUSHPULL(GPIOC_MEMS_CS) | PIN_OTYPE_PUSHPULL(GPIOC_PIN1) | PIN_OTYPE_PUSHPULL(GPIOC_PIN2) | PIN_OTYPE_PUSHPULL(GPIOC_PIN3) | PIN_OTYPE_PUSHPULL(GPIOC_PIN4) | PIN_OTYPE_PUSHPULL(GPIOC_PIN5) | PIN_OTYPE_PUSHPULL(GPIOC_LED_RED) | PIN_OTYPE_PUSHPULL(GPIOC_LED_BLUE) | PIN_OTYPE_PUSHPULL(GPIOC_LED_ORANGE) | PIN_OTYPE_PUSHPULL(GPIOC_LED_GREEN) | PIN_OTYPE_PUSHPULL(GPIOC_PIN10) | PIN_OTYPE_PUSHPULL(GPIOC_PIN11) | PIN_OTYPE_PUSHPULL(GPIOC_PIN12) | PIN_OTYPE_PUSHPULL(GPIOC_PIN13) | PIN_OTYPE_PUSHPULL(GPIOC_OSC32_IN) | PIN_OTYPE_PUSHPULL(GPIOC_OSC32_OUT)) -#define VAL_GPIOC_OSPEEDR (PIN_OSPEED_HIGH(GPIOC_MEMS_CS) | PIN_OSPEED_VERYLOW(GPIOC_PIN1) | PIN_OSPEED_VERYLOW(GPIOC_PIN2) | PIN_OSPEED_VERYLOW(GPIOC_PIN3) | PIN_OSPEED_VERYLOW(GPIOC_PIN4) | PIN_OSPEED_VERYLOW(GPIOC_PIN5) | PIN_OSPEED_HIGH(GPIOC_LED_RED) | PIN_OSPEED_HIGH(GPIOC_LED_BLUE) | PIN_OSPEED_HIGH(GPIOC_LED_ORANGE) | PIN_OSPEED_HIGH(GPIOC_LED_GREEN) | PIN_OSPEED_VERYLOW(GPIOC_PIN10) | PIN_OSPEED_VERYLOW(GPIOC_PIN11) | PIN_OSPEED_VERYLOW(GPIOC_PIN12) | PIN_OSPEED_VERYLOW(GPIOC_PIN13) | PIN_OSPEED_HIGH(GPIOC_OSC32_IN) | PIN_OSPEED_HIGH(GPIOC_OSC32_OUT)) -#define VAL_GPIOC_PUPDR (PIN_PUPDR_FLOATING(GPIOC_MEMS_CS) | PIN_PUPDR_PULLUP(GPIOC_PIN1) | PIN_PUPDR_PULLUP(GPIOC_PIN2) | PIN_PUPDR_PULLUP(GPIOC_PIN3) | PIN_PUPDR_PULLUP(GPIOC_PIN4) | PIN_PUPDR_PULLUP(GPIOC_PIN5) | PIN_PUPDR_FLOATING(GPIOC_LED_RED) | PIN_PUPDR_FLOATING(GPIOC_LED_BLUE) | PIN_PUPDR_FLOATING(GPIOC_LED_ORANGE) | PIN_PUPDR_FLOATING(GPIOC_LED_GREEN) | PIN_PUPDR_PULLUP(GPIOC_PIN10) | PIN_PUPDR_PULLUP(GPIOC_PIN11) | PIN_PUPDR_PULLUP(GPIOC_PIN12) | PIN_PUPDR_PULLUP(GPIOC_PIN13) | PIN_PUPDR_FLOATING(GPIOC_OSC32_IN) | PIN_PUPDR_FLOATING(GPIOC_OSC32_OUT)) -#define VAL_GPIOC_ODR (PIN_ODR_HIGH(GPIOC_MEMS_CS) | PIN_ODR_HIGH(GPIOC_PIN1) | PIN_ODR_HIGH(GPIOC_PIN2) | PIN_ODR_HIGH(GPIOC_PIN3) | PIN_ODR_HIGH(GPIOC_PIN4) | PIN_ODR_HIGH(GPIOC_PIN5) | PIN_ODR_LOW(GPIOC_LED_RED) | PIN_ODR_LOW(GPIOC_LED_BLUE) | PIN_ODR_LOW(GPIOC_LED_ORANGE) | PIN_ODR_LOW(GPIOC_LED_GREEN) | PIN_ODR_HIGH(GPIOC_PIN10) | PIN_ODR_HIGH(GPIOC_PIN11) | PIN_ODR_HIGH(GPIOC_PIN12) | PIN_ODR_HIGH(GPIOC_PIN13) | PIN_ODR_HIGH(GPIOC_OSC32_IN) | PIN_ODR_HIGH(GPIOC_OSC32_OUT)) -#define VAL_GPIOC_AFRL (PIN_AFIO_AF(GPIOC_MEMS_CS, 0U) | PIN_AFIO_AF(GPIOC_PIN1, 0U) | PIN_AFIO_AF(GPIOC_PIN2, 0U) | PIN_AFIO_AF(GPIOC_PIN3, 0U) | PIN_AFIO_AF(GPIOC_PIN4, 0U) | PIN_AFIO_AF(GPIOC_PIN5, 0U) | PIN_AFIO_AF(GPIOC_LED_RED, 0U) | PIN_AFIO_AF(GPIOC_LED_BLUE, 0U)) -#define VAL_GPIOC_AFRH (PIN_AFIO_AF(GPIOC_LED_ORANGE, 0U) | PIN_AFIO_AF(GPIOC_LED_GREEN, 0U) | PIN_AFIO_AF(GPIOC_PIN10, 0U) | PIN_AFIO_AF(GPIOC_PIN11, 0U) | PIN_AFIO_AF(GPIOC_PIN12, 0U) | PIN_AFIO_AF(GPIOC_PIN13, 0U) | PIN_AFIO_AF(GPIOC_OSC32_IN, 0U) | PIN_AFIO_AF(GPIOC_OSC32_OUT, 0U)) - -/* - * GPIOD setup: - * - * PD0 - PIN0 (input pullup). - * PD1 - PIN1 (input pullup). - * PD2 - PIN2 (input pullup). - * PD3 - PIN3 (input pullup). - * PD4 - PIN4 (input pullup). - * PD5 - PIN5 (input pullup). - * PD6 - PIN6 (input pullup). - * PD7 - PIN7 (input pullup). - * PD8 - PIN8 (input pullup). - * PD9 - PIN9 (input pullup). - * PD10 - PIN10 (input pullup). - * PD11 - PIN11 (input pullup). - * PD12 - PIN12 (input pullup). - * PD13 - PIN13 (input pullup). - * PD14 - PIN14 (input pullup). - * PD15 - PIN15 (input pullup). - */ -#define VAL_GPIOD_MODER (PIN_MODE_INPUT(GPIOD_PIN0) | PIN_MODE_INPUT(GPIOD_PIN1) | PIN_MODE_INPUT(GPIOD_PIN2) | PIN_MODE_INPUT(GPIOD_PIN3) | PIN_MODE_INPUT(GPIOD_PIN4) | PIN_MODE_INPUT(GPIOD_PIN5) | PIN_MODE_INPUT(GPIOD_PIN6) | PIN_MODE_INPUT(GPIOD_PIN7) | PIN_MODE_INPUT(GPIOD_PIN8) | PIN_MODE_INPUT(GPIOD_PIN9) | PIN_MODE_INPUT(GPIOD_PIN10) | PIN_MODE_INPUT(GPIOD_PIN11) | PIN_MODE_INPUT(GPIOD_PIN12) | PIN_MODE_INPUT(GPIOD_PIN13) | PIN_MODE_INPUT(GPIOD_PIN14) | PIN_MODE_INPUT(GPIOD_PIN15)) -#define VAL_GPIOD_OTYPER (PIN_OTYPE_PUSHPULL(GPIOD_PIN0) | PIN_OTYPE_PUSHPULL(GPIOD_PIN1) | PIN_OTYPE_PUSHPULL(GPIOD_PIN2) | PIN_OTYPE_PUSHPULL(GPIOD_PIN3) | PIN_OTYPE_PUSHPULL(GPIOD_PIN4) | PIN_OTYPE_PUSHPULL(GPIOD_PIN5) | PIN_OTYPE_PUSHPULL(GPIOD_PIN6) | PIN_OTYPE_PUSHPULL(GPIOD_PIN7) | PIN_OTYPE_PUSHPULL(GPIOD_PIN8) | PIN_OTYPE_PUSHPULL(GPIOD_PIN9) | PIN_OTYPE_PUSHPULL(GPIOD_PIN10) | PIN_OTYPE_PUSHPULL(GPIOD_PIN11) | PIN_OTYPE_PUSHPULL(GPIOD_PIN12) | PIN_OTYPE_PUSHPULL(GPIOD_PIN13) | PIN_OTYPE_PUSHPULL(GPIOD_PIN14) | PIN_OTYPE_PUSHPULL(GPIOD_PIN15)) -#define VAL_GPIOD_OSPEEDR (PIN_OSPEED_VERYLOW(GPIOD_PIN0) | PIN_OSPEED_VERYLOW(GPIOD_PIN1) | PIN_OSPEED_VERYLOW(GPIOD_PIN2) | PIN_OSPEED_VERYLOW(GPIOD_PIN3) | PIN_OSPEED_VERYLOW(GPIOD_PIN4) | PIN_OSPEED_VERYLOW(GPIOD_PIN5) | PIN_OSPEED_VERYLOW(GPIOD_PIN6) | PIN_OSPEED_VERYLOW(GPIOD_PIN7) | PIN_OSPEED_VERYLOW(GPIOD_PIN8) | PIN_OSPEED_VERYLOW(GPIOD_PIN9) | PIN_OSPEED_VERYLOW(GPIOD_PIN10) | PIN_OSPEED_VERYLOW(GPIOD_PIN11) | PIN_OSPEED_VERYLOW(GPIOD_PIN12) | PIN_OSPEED_VERYLOW(GPIOD_PIN13) | PIN_OSPEED_VERYLOW(GPIOD_PIN14) | PIN_OSPEED_VERYLOW(GPIOD_PIN15)) -#define VAL_GPIOD_PUPDR (PIN_PUPDR_PULLUP(GPIOD_PIN0) | PIN_PUPDR_PULLUP(GPIOD_PIN1) | PIN_PUPDR_PULLUP(GPIOD_PIN2) | PIN_PUPDR_PULLUP(GPIOD_PIN3) | PIN_PUPDR_PULLUP(GPIOD_PIN4) | PIN_PUPDR_PULLUP(GPIOD_PIN5) | PIN_PUPDR_PULLUP(GPIOD_PIN6) | PIN_PUPDR_PULLUP(GPIOD_PIN7) | PIN_PUPDR_PULLUP(GPIOD_PIN8) | PIN_PUPDR_PULLUP(GPIOD_PIN9) | PIN_PUPDR_PULLUP(GPIOD_PIN10) | PIN_PUPDR_PULLUP(GPIOD_PIN11) | PIN_PUPDR_PULLUP(GPIOD_PIN12) | PIN_PUPDR_PULLUP(GPIOD_PIN13) | PIN_PUPDR_PULLUP(GPIOD_PIN14) | PIN_PUPDR_PULLUP(GPIOD_PIN15)) -#define VAL_GPIOD_ODR (PIN_ODR_HIGH(GPIOD_PIN0) | PIN_ODR_HIGH(GPIOD_PIN1) | PIN_ODR_HIGH(GPIOD_PIN2) | PIN_ODR_HIGH(GPIOD_PIN3) | PIN_ODR_HIGH(GPIOD_PIN4) | PIN_ODR_HIGH(GPIOD_PIN5) | PIN_ODR_HIGH(GPIOD_PIN6) | PIN_ODR_HIGH(GPIOD_PIN7) | PIN_ODR_HIGH(GPIOD_PIN8) | PIN_ODR_HIGH(GPIOD_PIN9) | PIN_ODR_HIGH(GPIOD_PIN10) | PIN_ODR_HIGH(GPIOD_PIN11) | PIN_ODR_HIGH(GPIOD_PIN12) | PIN_ODR_HIGH(GPIOD_PIN13) | PIN_ODR_HIGH(GPIOD_PIN14) | PIN_ODR_HIGH(GPIOD_PIN15)) -#define VAL_GPIOD_AFRL (PIN_AFIO_AF(GPIOD_PIN0, 0U) | PIN_AFIO_AF(GPIOD_PIN1, 0U) | PIN_AFIO_AF(GPIOD_PIN2, 0U) | PIN_AFIO_AF(GPIOD_PIN3, 0U) | PIN_AFIO_AF(GPIOD_PIN4, 0U) | PIN_AFIO_AF(GPIOD_PIN5, 0U) | PIN_AFIO_AF(GPIOD_PIN6, 0U) | PIN_AFIO_AF(GPIOD_PIN7, 0U)) -#define VAL_GPIOD_AFRH (PIN_AFIO_AF(GPIOD_PIN8, 0U) | PIN_AFIO_AF(GPIOD_PIN9, 0U) | PIN_AFIO_AF(GPIOD_PIN10, 0U) | PIN_AFIO_AF(GPIOD_PIN11, 0U) | PIN_AFIO_AF(GPIOD_PIN12, 0U) | PIN_AFIO_AF(GPIOD_PIN13, 0U) | PIN_AFIO_AF(GPIOD_PIN14, 0U) | PIN_AFIO_AF(GPIOD_PIN15, 0U)) - -/* - * GPIOE setup: - * - * PE0 - PIN0 (input pullup). - * PE1 - PIN1 (input pullup). - * PE2 - PIN2 (input pullup). - * PE3 - PIN3 (input pullup). - * PE4 - PIN4 (input pullup). - * PE5 - PIN5 (input pullup). - * PE6 - PIN6 (input pullup). - * PE7 - PIN7 (input pullup). - * PE8 - PIN8 (input pullup). - * PE9 - PIN9 (input pullup). - * PE10 - PIN10 (input pullup). - * PE11 - PIN11 (input pullup). - * PE12 - PIN12 (input pullup). - * PE13 - PIN13 (input pullup). - * PE14 - PIN14 (input pullup). - * PE15 - PIN15 (input pullup). - */ -#define VAL_GPIOE_MODER (PIN_MODE_INPUT(GPIOE_PIN0) | PIN_MODE_INPUT(GPIOE_PIN1) | PIN_MODE_INPUT(GPIOE_PIN2) | PIN_MODE_INPUT(GPIOE_PIN3) | PIN_MODE_INPUT(GPIOE_PIN4) | PIN_MODE_INPUT(GPIOE_PIN5) | PIN_MODE_INPUT(GPIOE_PIN6) | PIN_MODE_INPUT(GPIOE_PIN7) | PIN_MODE_INPUT(GPIOE_PIN8) | PIN_MODE_INPUT(GPIOE_PIN9) | PIN_MODE_INPUT(GPIOE_PIN10) | PIN_MODE_INPUT(GPIOE_PIN11) | PIN_MODE_INPUT(GPIOE_PIN12) | PIN_MODE_INPUT(GPIOE_PIN13) | PIN_MODE_INPUT(GPIOE_PIN14) | PIN_MODE_INPUT(GPIOE_PIN15)) -#define VAL_GPIOE_OTYPER (PIN_OTYPE_PUSHPULL(GPIOE_PIN0) | PIN_OTYPE_PUSHPULL(GPIOE_PIN1) | PIN_OTYPE_PUSHPULL(GPIOE_PIN2) | PIN_OTYPE_PUSHPULL(GPIOE_PIN3) | PIN_OTYPE_PUSHPULL(GPIOE_PIN4) | PIN_OTYPE_PUSHPULL(GPIOE_PIN5) | PIN_OTYPE_PUSHPULL(GPIOE_PIN6) | PIN_OTYPE_PUSHPULL(GPIOE_PIN7) | PIN_OTYPE_PUSHPULL(GPIOE_PIN8) | PIN_OTYPE_PUSHPULL(GPIOE_PIN9) | PIN_OTYPE_PUSHPULL(GPIOE_PIN10) | PIN_OTYPE_PUSHPULL(GPIOE_PIN11) | PIN_OTYPE_PUSHPULL(GPIOE_PIN12) | PIN_OTYPE_PUSHPULL(GPIOE_PIN13) | PIN_OTYPE_PUSHPULL(GPIOE_PIN14) | PIN_OTYPE_PUSHPULL(GPIOE_PIN15)) -#define VAL_GPIOE_OSPEEDR (PIN_OSPEED_VERYLOW(GPIOE_PIN0) | PIN_OSPEED_VERYLOW(GPIOE_PIN1) | PIN_OSPEED_VERYLOW(GPIOE_PIN2) | PIN_OSPEED_VERYLOW(GPIOE_PIN3) | PIN_OSPEED_VERYLOW(GPIOE_PIN4) | PIN_OSPEED_VERYLOW(GPIOE_PIN5) | PIN_OSPEED_VERYLOW(GPIOE_PIN6) | PIN_OSPEED_VERYLOW(GPIOE_PIN7) | PIN_OSPEED_VERYLOW(GPIOE_PIN8) | PIN_OSPEED_VERYLOW(GPIOE_PIN9) | PIN_OSPEED_VERYLOW(GPIOE_PIN10) | PIN_OSPEED_VERYLOW(GPIOE_PIN11) | PIN_OSPEED_VERYLOW(GPIOE_PIN12) | PIN_OSPEED_VERYLOW(GPIOE_PIN13) | PIN_OSPEED_VERYLOW(GPIOE_PIN14) | PIN_OSPEED_VERYLOW(GPIOE_PIN15)) -#define VAL_GPIOE_PUPDR (PIN_PUPDR_PULLUP(GPIOE_PIN0) | PIN_PUPDR_PULLUP(GPIOE_PIN1) | PIN_PUPDR_PULLUP(GPIOE_PIN2) | PIN_PUPDR_PULLUP(GPIOE_PIN3) | PIN_PUPDR_PULLUP(GPIOE_PIN4) | PIN_PUPDR_PULLUP(GPIOE_PIN5) | PIN_PUPDR_PULLUP(GPIOE_PIN6) | PIN_PUPDR_PULLUP(GPIOE_PIN7) | PIN_PUPDR_PULLUP(GPIOE_PIN8) | PIN_PUPDR_PULLUP(GPIOE_PIN9) | PIN_PUPDR_PULLUP(GPIOE_PIN10) | PIN_PUPDR_PULLUP(GPIOE_PIN11) | PIN_PUPDR_PULLUP(GPIOE_PIN12) | PIN_PUPDR_PULLUP(GPIOE_PIN13) | PIN_PUPDR_PULLUP(GPIOE_PIN14) | PIN_PUPDR_PULLUP(GPIOE_PIN15)) -#define VAL_GPIOE_ODR (PIN_ODR_HIGH(GPIOE_PIN0) | PIN_ODR_HIGH(GPIOE_PIN1) | PIN_ODR_HIGH(GPIOE_PIN2) | PIN_ODR_HIGH(GPIOE_PIN3) | PIN_ODR_HIGH(GPIOE_PIN4) | PIN_ODR_HIGH(GPIOE_PIN5) | PIN_ODR_HIGH(GPIOE_PIN6) | PIN_ODR_HIGH(GPIOE_PIN7) | PIN_ODR_HIGH(GPIOE_PIN8) | PIN_ODR_HIGH(GPIOE_PIN9) | PIN_ODR_HIGH(GPIOE_PIN10) | PIN_ODR_HIGH(GPIOE_PIN11) | PIN_ODR_HIGH(GPIOE_PIN12) | PIN_ODR_HIGH(GPIOE_PIN13) | PIN_ODR_HIGH(GPIOE_PIN14) | PIN_ODR_HIGH(GPIOE_PIN15)) -#define VAL_GPIOE_AFRL (PIN_AFIO_AF(GPIOE_PIN0, 0U) | PIN_AFIO_AF(GPIOE_PIN1, 0U) | PIN_AFIO_AF(GPIOE_PIN2, 0U) | PIN_AFIO_AF(GPIOE_PIN3, 0U) | PIN_AFIO_AF(GPIOE_PIN4, 0U) | PIN_AFIO_AF(GPIOE_PIN5, 0U) | PIN_AFIO_AF(GPIOE_PIN6, 0U) | PIN_AFIO_AF(GPIOE_PIN7, 0U)) -#define VAL_GPIOE_AFRH (PIN_AFIO_AF(GPIOE_PIN8, 0U) | PIN_AFIO_AF(GPIOE_PIN9, 0U) | PIN_AFIO_AF(GPIOE_PIN10, 0U) | PIN_AFIO_AF(GPIOE_PIN11, 0U) | PIN_AFIO_AF(GPIOE_PIN12, 0U) | PIN_AFIO_AF(GPIOE_PIN13, 0U) | PIN_AFIO_AF(GPIOE_PIN14, 0U) | PIN_AFIO_AF(GPIOE_PIN15, 0U)) - -/* - * GPIOF setup: - * - * PF0 - OSC_IN (input floating). - * PF1 - OSC_OUT (input floating). - * PF2 - PIN2 (input pullup). - * PF3 - PIN3 (input pullup). - * PF4 - PIN4 (input pullup). - * PF5 - PIN5 (input pullup). - * PF6 - PIN6 (input pullup). - * PF7 - PIN7 (input pullup). - * PF8 - PIN8 (input pullup). - * PF9 - PIN9 (input pullup). - * PF10 - PIN10 (input pullup). - * PF11 - PIN11 (input pullup). - * PF12 - PIN12 (input pullup). - * PF13 - PIN13 (input pullup). - * PF14 - PIN14 (input pullup). - * PF15 - PIN15 (input pullup). - */ -#define VAL_GPIOF_MODER (PIN_MODE_INPUT(GPIOF_OSC_IN) | PIN_MODE_INPUT(GPIOF_OSC_OUT) | PIN_MODE_INPUT(GPIOF_PIN2) | PIN_MODE_INPUT(GPIOF_PIN3) | PIN_MODE_INPUT(GPIOF_PIN4) | PIN_MODE_INPUT(GPIOF_PIN5) | PIN_MODE_INPUT(GPIOF_PIN6) | PIN_MODE_INPUT(GPIOF_PIN7) | PIN_MODE_INPUT(GPIOF_PIN8) | PIN_MODE_INPUT(GPIOF_PIN9) | PIN_MODE_INPUT(GPIOF_PIN10) | PIN_MODE_INPUT(GPIOF_PIN11) | PIN_MODE_INPUT(GPIOF_PIN12) | PIN_MODE_INPUT(GPIOF_PIN13) | PIN_MODE_INPUT(GPIOF_PIN14) | PIN_MODE_INPUT(GPIOF_PIN15)) -#define VAL_GPIOF_OTYPER (PIN_OTYPE_PUSHPULL(GPIOF_OSC_IN) | PIN_OTYPE_PUSHPULL(GPIOF_OSC_OUT) | PIN_OTYPE_PUSHPULL(GPIOF_PIN2) | PIN_OTYPE_PUSHPULL(GPIOF_PIN3) | PIN_OTYPE_PUSHPULL(GPIOF_PIN4) | PIN_OTYPE_PUSHPULL(GPIOF_PIN5) | PIN_OTYPE_PUSHPULL(GPIOF_PIN6) | PIN_OTYPE_PUSHPULL(GPIOF_PIN7) | PIN_OTYPE_PUSHPULL(GPIOF_PIN8) | PIN_OTYPE_PUSHPULL(GPIOF_PIN9) | PIN_OTYPE_PUSHPULL(GPIOF_PIN10) | PIN_OTYPE_PUSHPULL(GPIOF_PIN11) | PIN_OTYPE_PUSHPULL(GPIOF_PIN12) | PIN_OTYPE_PUSHPULL(GPIOF_PIN13) | PIN_OTYPE_PUSHPULL(GPIOF_PIN14) | PIN_OTYPE_PUSHPULL(GPIOF_PIN15)) -#define VAL_GPIOF_OSPEEDR (PIN_OSPEED_VERYLOW(GPIOF_OSC_IN) | PIN_OSPEED_VERYLOW(GPIOF_OSC_OUT) | PIN_OSPEED_VERYLOW(GPIOF_PIN2) | PIN_OSPEED_VERYLOW(GPIOF_PIN3) | PIN_OSPEED_VERYLOW(GPIOF_PIN4) | PIN_OSPEED_VERYLOW(GPIOF_PIN5) | PIN_OSPEED_VERYLOW(GPIOF_PIN6) | PIN_OSPEED_VERYLOW(GPIOF_PIN7) | PIN_OSPEED_VERYLOW(GPIOF_PIN8) | PIN_OSPEED_VERYLOW(GPIOF_PIN9) | PIN_OSPEED_VERYLOW(GPIOF_PIN10) | PIN_OSPEED_VERYLOW(GPIOF_PIN11) | PIN_OSPEED_VERYLOW(GPIOF_PIN12) | PIN_OSPEED_VERYLOW(GPIOF_PIN13) | PIN_OSPEED_VERYLOW(GPIOF_PIN14) | PIN_OSPEED_VERYLOW(GPIOF_PIN15)) -#define VAL_GPIOF_PUPDR (PIN_PUPDR_FLOATING(GPIOF_OSC_IN) | PIN_PUPDR_FLOATING(GPIOF_OSC_OUT) | PIN_PUPDR_PULLUP(GPIOF_PIN2) | PIN_PUPDR_PULLUP(GPIOF_PIN3) | PIN_PUPDR_PULLUP(GPIOF_PIN4) | PIN_PUPDR_PULLUP(GPIOF_PIN5) | PIN_PUPDR_PULLUP(GPIOF_PIN6) | PIN_PUPDR_PULLUP(GPIOF_PIN7) | PIN_PUPDR_PULLUP(GPIOF_PIN8) | PIN_PUPDR_PULLUP(GPIOF_PIN9) | PIN_PUPDR_PULLUP(GPIOF_PIN10) | PIN_PUPDR_PULLUP(GPIOF_PIN11) | PIN_PUPDR_PULLUP(GPIOF_PIN12) | PIN_PUPDR_PULLUP(GPIOF_PIN13) | PIN_PUPDR_PULLUP(GPIOF_PIN14) | PIN_PUPDR_PULLUP(GPIOF_PIN15)) -#define VAL_GPIOF_ODR (PIN_ODR_HIGH(GPIOF_OSC_IN) | PIN_ODR_HIGH(GPIOF_OSC_OUT) | PIN_ODR_HIGH(GPIOF_PIN2) | PIN_ODR_HIGH(GPIOF_PIN3) | PIN_ODR_HIGH(GPIOF_PIN4) | PIN_ODR_HIGH(GPIOF_PIN5) | PIN_ODR_HIGH(GPIOF_PIN6) | PIN_ODR_HIGH(GPIOF_PIN7) | PIN_ODR_HIGH(GPIOF_PIN8) | PIN_ODR_HIGH(GPIOF_PIN9) | PIN_ODR_HIGH(GPIOF_PIN10) | PIN_ODR_HIGH(GPIOF_PIN11) | PIN_ODR_HIGH(GPIOF_PIN12) | PIN_ODR_HIGH(GPIOF_PIN13) | PIN_ODR_HIGH(GPIOF_PIN14) | PIN_ODR_HIGH(GPIOF_PIN15)) -#define VAL_GPIOF_AFRL (PIN_AFIO_AF(GPIOF_OSC_IN, 0U) | PIN_AFIO_AF(GPIOF_OSC_OUT, 0U) | PIN_AFIO_AF(GPIOF_PIN2, 0U) | PIN_AFIO_AF(GPIOF_PIN3, 0U) | PIN_AFIO_AF(GPIOF_PIN4, 0U) | PIN_AFIO_AF(GPIOF_PIN5, 0U) | PIN_AFIO_AF(GPIOF_PIN6, 0U) | PIN_AFIO_AF(GPIOF_PIN7, 0U)) -#define VAL_GPIOF_AFRH (PIN_AFIO_AF(GPIOF_PIN8, 0U) | PIN_AFIO_AF(GPIOF_PIN9, 0U) | PIN_AFIO_AF(GPIOF_PIN10, 0U) | PIN_AFIO_AF(GPIOF_PIN11, 0U) | PIN_AFIO_AF(GPIOF_PIN12, 0U) | PIN_AFIO_AF(GPIOF_PIN13, 0U) | PIN_AFIO_AF(GPIOF_PIN14, 0U) | PIN_AFIO_AF(GPIOF_PIN15, 0U)) - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if !defined(_FROM_ASM_) -# ifdef __cplusplus -extern "C" { -# endif -void boardInit(void); -# ifdef __cplusplus -} -# endif -#endif /* _FROM_ASM_ */ - -#endif /* BOARD_H */ diff --git a/platforms/chibios/GENERIC_STM32_F072XB/board/board.mk b/platforms/chibios/GENERIC_STM32_F072XB/board/board.mk index 842e33590594..3f0e6c46e8c0 100644 --- a/platforms/chibios/GENERIC_STM32_F072XB/board/board.mk +++ b/platforms/chibios/GENERIC_STM32_F072XB/board/board.mk @@ -1,8 +1,8 @@ # List of all the board related files. -BOARDSRC = $(BOARD_PATH)/board/board.c +BOARDSRC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_F072RB/board.c # Required include directories -BOARDINC = $(BOARD_PATH)/board +BOARDINC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_F072RB # Shared variables ALLCSRC += $(BOARDSRC) diff --git a/platforms/chibios/GENERIC_STM32_F072XB/configs/board.h b/platforms/chibios/GENERIC_STM32_F072XB/configs/board.h new file mode 100644 index 000000000000..30af6b0c8608 --- /dev/null +++ b/platforms/chibios/GENERIC_STM32_F072XB/configs/board.h @@ -0,0 +1,20 @@ +/* Copyright 2020 Nick Brassel (tzarc) + * + * 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 3 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 . + */ +#pragma once + +#include_next "board.h" + +#undef STM32_HSE_BYPASS diff --git a/platforms/chibios/GENERIC_STM32_F072XB/configs/mcuconf.h b/platforms/chibios/GENERIC_STM32_F072XB/configs/mcuconf.h new file mode 100644 index 000000000000..688350e9ce68 --- /dev/null +++ b/platforms/chibios/GENERIC_STM32_F072XB/configs/mcuconf.h @@ -0,0 +1,177 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef _MCUCONF_H_ +#define _MCUCONF_H_ + +/* + * STM32F0xx drivers configuration. + * The following settings override the default settings present in + * the various device driver implementation headers. + * Note that the settings for each driver only have effect if the whole + * driver is enabled in halconf.h. + * + * IRQ priorities: + * 3...0 Lowest...Highest. + * + * DMA priorities: + * 0...3 Lowest...Highest. + */ + +#define STM32F0xx_MCUCONF +// #define STM32F070xB + +/* + * HAL driver system settings. + */ +#define STM32_NO_INIT FALSE +#define STM32_PVD_ENABLE FALSE +#define STM32_PLS STM32_PLS_LEV0 +#define STM32_HSI_ENABLED TRUE +#define STM32_HSI14_ENABLED TRUE +#define STM32_HSI48_ENABLED FALSE +#define STM32_LSI_ENABLED TRUE +#define STM32_HSE_ENABLED FALSE +#define STM32_LSE_ENABLED FALSE +#define STM32_SW STM32_SW_PLL +#define STM32_PLLSRC STM32_PLLSRC_HSI_DIV2 +#define STM32_PREDIV_VALUE 1 +#define STM32_PLLMUL_VALUE 12 +#define STM32_HPRE STM32_HPRE_DIV1 +#define STM32_PPRE STM32_PPRE_DIV1 +#define STM32_ADCSW STM32_ADCSW_HSI14 +#define STM32_ADCPRE STM32_ADCPRE_DIV4 +#define STM32_MCOSEL STM32_MCOSEL_NOCLOCK +#define STM32_ADCPRE STM32_ADCPRE_DIV4 +#define STM32_ADCSW STM32_ADCSW_HSI14 +#define STM32_USBSW STM32_USBSW_HSI48 +#define STM32_CECSW STM32_CECSW_HSI +#define STM32_I2C1SW STM32_I2C1SW_HSI +#define STM32_USART1SW STM32_USART1SW_PCLK +#define STM32_RTCSEL STM32_RTCSEL_LSI + +/* + * IRQ system settings. + */ +#define STM32_IRQ_EXTI0_1_IRQ_PRIORITY 3 +#define STM32_IRQ_EXTI2_3_IRQ_PRIORITY 3 +#define STM32_IRQ_EXTI4_15_IRQ_PRIORITY 3 +#define STM32_IRQ_EXTI16_IRQ_PRIORITY 3 +#define STM32_IRQ_EXTI17_20_IRQ_PRIORITY 3 +#define STM32_IRQ_EXTI21_22_IRQ_PRIORITY 3 + +/* + * ADC driver system settings. + */ +#define STM32_ADC_USE_ADC1 FALSE +#define STM32_ADC_ADC1_DMA_PRIORITY 2 +#define STM32_ADC_IRQ_PRIORITY 2 +#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY 2 + +/* + * GPT driver system settings. + */ +#define STM32_GPT_USE_TIM1 FALSE +#define STM32_GPT_USE_TIM2 FALSE +#define STM32_GPT_USE_TIM3 FALSE +#define STM32_GPT_USE_TIM14 FALSE +#define STM32_GPT_TIM1_IRQ_PRIORITY 2 +#define STM32_GPT_TIM2_IRQ_PRIORITY 2 +#define STM32_GPT_TIM3_IRQ_PRIORITY 2 +#define STM32_GPT_TIM14_IRQ_PRIORITY 2 + +/* + * I2C driver system settings. + */ +#define STM32_I2C_USE_I2C1 FALSE +#define STM32_I2C_USE_I2C2 FALSE +#define STM32_I2C_BUSY_TIMEOUT 50 +#define STM32_I2C_I2C1_IRQ_PRIORITY 3 +#define STM32_I2C_I2C2_IRQ_PRIORITY 3 +#define STM32_I2C_USE_DMA TRUE +#define STM32_I2C_I2C1_DMA_PRIORITY 1 +#define STM32_I2C_I2C2_DMA_PRIORITY 1 +#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7) +#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6) +#define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure") + +/* + * ICU driver system settings. + */ +#define STM32_ICU_USE_TIM1 FALSE +#define STM32_ICU_USE_TIM2 FALSE +#define STM32_ICU_USE_TIM3 FALSE +#define STM32_ICU_TIM1_IRQ_PRIORITY 3 +#define STM32_ICU_TIM2_IRQ_PRIORITY 3 +#define STM32_ICU_TIM3_IRQ_PRIORITY 3 + +/* + * PWM driver system settings. + */ +#define STM32_PWM_USE_ADVANCED FALSE +#define STM32_PWM_USE_TIM1 FALSE +#define STM32_PWM_USE_TIM2 FALSE +#define STM32_PWM_USE_TIM3 TRUE +#define STM32_PWM_TIM1_IRQ_PRIORITY 3 +#define STM32_PWM_TIM2_IRQ_PRIORITY 3 +#define STM32_PWM_TIM3_IRQ_PRIORITY 3 + +/* + * SERIAL driver system settings. + */ +#define STM32_SERIAL_USE_USART1 FALSE +#define STM32_SERIAL_USE_USART2 FALSE +#define STM32_SERIAL_USART1_PRIORITY 3 +#define STM32_SERIAL_USART2_PRIORITY 3 + +/* + * SPI driver system settings. + */ +#define STM32_SPI_USE_SPI1 FALSE +#define STM32_SPI_USE_SPI2 TRUE +#define STM32_SPI_SPI1_DMA_PRIORITY 1 +#define STM32_SPI_SPI2_DMA_PRIORITY 1 +#define STM32_SPI_SPI1_IRQ_PRIORITY 2 +#define STM32_SPI_SPI2_IRQ_PRIORITY 2 +#define STM32_SPI_SPI2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4) +#define STM32_SPI_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5) +#define STM32_SPI_DMA_ERROR_HOOK(spip) osalSysHalt("DMA failure") + +/* + * ST driver system settings. + */ +#define STM32_ST_IRQ_PRIORITY 2 +#define STM32_ST_USE_TIMER 2 + +/* + * UART driver system settings. + */ +#define STM32_UART_USE_USART1 FALSE +#define STM32_UART_USE_USART2 FALSE +#define STM32_UART_USART1_IRQ_PRIORITY 3 +#define STM32_UART_USART2_IRQ_PRIORITY 3 +#define STM32_UART_USART1_DMA_PRIORITY 0 +#define STM32_UART_USART2_DMA_PRIORITY 0 +#define STM32_UART_DMA_ERROR_HOOK(uartp) osalSysHalt("DMA failure") + +/* + * USB driver system settings. + */ +#define STM32_USB_USE_USB1 TRUE +#define STM32_USB_LOW_POWER_ON_SUSPEND FALSE +#define STM32_USB_USB1_LP_IRQ_PRIORITY 3 + +#endif /* _MCUCONF_H_ */ diff --git a/platforms/chibios/GENERIC_STM32_F303XC/configs/config.h b/platforms/chibios/GENERIC_STM32_F303XC/configs/config.h index 48a7a900930a..a73f0c0b475a 100644 --- a/platforms/chibios/GENERIC_STM32_F303XC/configs/config.h +++ b/platforms/chibios/GENERIC_STM32_F303XC/configs/config.h @@ -15,4 +15,6 @@ */ #pragma once -#define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE +#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP +# define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE +#endif diff --git a/platforms/chibios/STM32_F103_STM32DUINO/configs/mcuconf.h b/platforms/chibios/STM32_F103_STM32DUINO/configs/mcuconf.h new file mode 100644 index 000000000000..9945e7408d12 --- /dev/null +++ b/platforms/chibios/STM32_F103_STM32DUINO/configs/mcuconf.h @@ -0,0 +1,209 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef _MCUCONF_H_ +#define _MCUCONF_H_ + +#define STM32F103_MCUCONF + +/* + * STM32F103 drivers configuration. + * The following settings override the default settings present in + * the various device driver implementation headers. + * Note that the settings for each driver only have effect if the whole + * driver is enabled in halconf.h. + * + * IRQ priorities: + * 15...0 Lowest...Highest. + * + * DMA priorities: + * 0...3 Lowest...Highest. + */ + +/* + * HAL driver system settings. + */ +#define STM32_NO_INIT FALSE +#define STM32_HSI_ENABLED TRUE +#define STM32_LSI_ENABLED FALSE +#define STM32_HSE_ENABLED TRUE +#define STM32_LSE_ENABLED FALSE +#define STM32_SW STM32_SW_PLL +#define STM32_PLLSRC STM32_PLLSRC_HSE +#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1 +#define STM32_PLLMUL_VALUE 9 +#define STM32_HPRE STM32_HPRE_DIV1 +#define STM32_PPRE1 STM32_PPRE1_DIV2 +#define STM32_PPRE2 STM32_PPRE2_DIV2 +#define STM32_ADCPRE STM32_ADCPRE_DIV4 +#define STM32_USB_CLOCK_REQUIRED TRUE +#define STM32_USBPRE STM32_USBPRE_DIV1P5 +#define STM32_MCOSEL STM32_MCOSEL_NOCLOCK +#define STM32_RTCSEL STM32_RTCSEL_HSEDIV +#define STM32_PVD_ENABLE FALSE +#define STM32_PLS STM32_PLS_LEV0 + +/* + * ADC driver system settings. + */ +#define STM32_ADC_USE_ADC1 FALSE +#define STM32_ADC_ADC1_DMA_PRIORITY 2 +#define STM32_ADC_ADC1_IRQ_PRIORITY 6 + +/* + * CAN driver system settings. + */ +#define STM32_CAN_USE_CAN1 FALSE +#define STM32_CAN_CAN1_IRQ_PRIORITY 11 + +/* + * EXT driver system settings. + */ +#define STM32_EXT_EXTI0_IRQ_PRIORITY 6 +#define STM32_EXT_EXTI1_IRQ_PRIORITY 6 +#define STM32_EXT_EXTI2_IRQ_PRIORITY 6 +#define STM32_EXT_EXTI3_IRQ_PRIORITY 6 +#define STM32_EXT_EXTI4_IRQ_PRIORITY 6 +#define STM32_EXT_EXTI5_9_IRQ_PRIORITY 6 +#define STM32_EXT_EXTI10_15_IRQ_PRIORITY 6 +#define STM32_EXT_EXTI16_IRQ_PRIORITY 6 +#define STM32_EXT_EXTI17_IRQ_PRIORITY 6 +#define STM32_EXT_EXTI18_IRQ_PRIORITY 6 +#define STM32_EXT_EXTI19_IRQ_PRIORITY 6 + +/* + * GPT driver system settings. + */ +#define STM32_GPT_USE_TIM1 FALSE +#define STM32_GPT_USE_TIM2 FALSE +#define STM32_GPT_USE_TIM3 FALSE +#define STM32_GPT_USE_TIM4 FALSE +#define STM32_GPT_USE_TIM5 FALSE +#define STM32_GPT_USE_TIM8 FALSE +#define STM32_GPT_TIM1_IRQ_PRIORITY 7 +#define STM32_GPT_TIM2_IRQ_PRIORITY 7 +#define STM32_GPT_TIM3_IRQ_PRIORITY 7 +#define STM32_GPT_TIM4_IRQ_PRIORITY 7 +#define STM32_GPT_TIM5_IRQ_PRIORITY 7 +#define STM32_GPT_TIM8_IRQ_PRIORITY 7 + +/* + * I2C driver system settings. + */ +#define STM32_I2C_USE_I2C1 FALSE +#define STM32_I2C_USE_I2C2 FALSE +#define STM32_I2C_BUSY_TIMEOUT 50 +#define STM32_I2C_I2C1_IRQ_PRIORITY 5 +#define STM32_I2C_I2C2_IRQ_PRIORITY 5 +#define STM32_I2C_I2C1_DMA_PRIORITY 3 +#define STM32_I2C_I2C2_DMA_PRIORITY 3 +#define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure") + +/* + * ICU driver system settings. + */ +#define STM32_ICU_USE_TIM1 FALSE +#define STM32_ICU_USE_TIM2 FALSE +#define STM32_ICU_USE_TIM3 FALSE +#define STM32_ICU_USE_TIM4 FALSE +#define STM32_ICU_USE_TIM5 FALSE +#define STM32_ICU_USE_TIM8 FALSE +#define STM32_ICU_TIM1_IRQ_PRIORITY 7 +#define STM32_ICU_TIM2_IRQ_PRIORITY 7 +#define STM32_ICU_TIM3_IRQ_PRIORITY 7 +#define STM32_ICU_TIM4_IRQ_PRIORITY 7 +#define STM32_ICU_TIM5_IRQ_PRIORITY 7 +#define STM32_ICU_TIM8_IRQ_PRIORITY 7 + +/* + * PWM driver system settings. + */ +#define STM32_PWM_USE_ADVANCED FALSE +#define STM32_PWM_USE_TIM1 FALSE +#define STM32_PWM_USE_TIM2 FALSE +#define STM32_PWM_USE_TIM3 FALSE +#define STM32_PWM_USE_TIM4 FALSE +#define STM32_PWM_USE_TIM5 FALSE +#define STM32_PWM_USE_TIM8 FALSE +#define STM32_PWM_TIM1_IRQ_PRIORITY 7 +#define STM32_PWM_TIM2_IRQ_PRIORITY 7 +#define STM32_PWM_TIM3_IRQ_PRIORITY 7 +#define STM32_PWM_TIM4_IRQ_PRIORITY 7 +#define STM32_PWM_TIM5_IRQ_PRIORITY 7 +#define STM32_PWM_TIM8_IRQ_PRIORITY 7 + +/* + * RTC driver system settings. + */ +#define STM32_RTC_IRQ_PRIORITY 15 + +/* + * SERIAL driver system settings. + */ +#define STM32_SERIAL_USE_USART1 FALSE +#define STM32_SERIAL_USE_USART2 FALSE +#define STM32_SERIAL_USE_USART3 FALSE +#define STM32_SERIAL_USE_UART4 FALSE +#define STM32_SERIAL_USE_UART5 FALSE +#define STM32_SERIAL_USART1_PRIORITY 12 +#define STM32_SERIAL_USART2_PRIORITY 12 +#define STM32_SERIAL_USART3_PRIORITY 12 +#define STM32_SERIAL_UART4_PRIORITY 12 +#define STM32_SERIAL_UART5_PRIORITY 12 + +/* + * SPI driver system settings. + */ +#define STM32_SPI_USE_SPI1 FALSE +#define STM32_SPI_USE_SPI2 TRUE +#define STM32_SPI_USE_SPI3 FALSE +#define STM32_SPI_SPI1_DMA_PRIORITY 1 +#define STM32_SPI_SPI2_DMA_PRIORITY 1 +#define STM32_SPI_SPI3_DMA_PRIORITY 1 +#define STM32_SPI_SPI1_IRQ_PRIORITY 10 +#define STM32_SPI_SPI2_IRQ_PRIORITY 10 +#define STM32_SPI_SPI3_IRQ_PRIORITY 10 +#define STM32_SPI_DMA_ERROR_HOOK(spip) osalSysHalt("DMA failure") + +/* + * ST driver system settings. + */ +#define STM32_ST_IRQ_PRIORITY 8 +#define STM32_ST_USE_TIMER 2 + +/* + * UART driver system settings. + */ +#define STM32_UART_USE_USART1 FALSE +#define STM32_UART_USE_USART2 FALSE +#define STM32_UART_USE_USART3 FALSE +#define STM32_UART_USART1_IRQ_PRIORITY 12 +#define STM32_UART_USART2_IRQ_PRIORITY 12 +#define STM32_UART_USART3_IRQ_PRIORITY 12 +#define STM32_UART_USART1_DMA_PRIORITY 0 +#define STM32_UART_USART2_DMA_PRIORITY 0 +#define STM32_UART_USART3_DMA_PRIORITY 0 +#define STM32_UART_DMA_ERROR_HOOK(uartp) osalSysHalt("DMA failure") + +/* + * USB driver system settings. + */ +#define STM32_USB_USE_USB1 TRUE +#define STM32_USB_LOW_POWER_ON_SUSPEND FALSE +#define STM32_USB_USB1_HP_IRQ_PRIORITY 13 +#define STM32_USB_USB1_LP_IRQ_PRIORITY 14 + +#endif /* _MCUCONF_H_ */ diff --git a/platforms/chibios/GENERIC_STM32_F303XC/configs/chconf.h b/platforms/chibios/common/configs/chconf.h similarity index 100% rename from platforms/chibios/GENERIC_STM32_F303XC/configs/chconf.h rename to platforms/chibios/common/configs/chconf.h diff --git a/platforms/chibios/GENERIC_STM32_F303XC/configs/halconf.h b/platforms/chibios/common/configs/halconf.h similarity index 100% rename from platforms/chibios/GENERIC_STM32_F303XC/configs/halconf.h rename to platforms/chibios/common/configs/halconf.h diff --git a/platforms/chibios/ld/MKL26Z64.ld b/platforms/chibios/common/ld/MKL26Z64.ld similarity index 100% rename from platforms/chibios/ld/MKL26Z64.ld rename to platforms/chibios/common/ld/MKL26Z64.ld diff --git a/platforms/chibios/ld/STM32F103x8_stm32duino_bootloader.ld b/platforms/chibios/common/ld/STM32F103x8_stm32duino_bootloader.ld similarity index 100% rename from platforms/chibios/ld/STM32F103x8_stm32duino_bootloader.ld rename to platforms/chibios/common/ld/STM32F103x8_stm32duino_bootloader.ld diff --git a/quantum/backlight/backlight_avr.c b/quantum/backlight/backlight_avr.c index b3e882ffe128..4d66da80ba15 100644 --- a/quantum/backlight/backlight_avr.c +++ b/quantum/backlight/backlight_avr.c @@ -3,6 +3,11 @@ #include "backlight_driver_common.h" #include "debug.h" +// Maximum duty cycle limit +#ifndef BACKLIGHT_LIMIT_VAL +# define BACKLIGHT_LIMIT_VAL 255 +#endif + // This logic is a bit complex, we support 3 setups: // // 1. Hardware PWM when backlight is wired to a PWM pin. @@ -240,6 +245,9 @@ static uint16_t cie_lightness(uint16_t v) { } } +// rescale the supplied backlight value to be in terms of the value limit +static uint32_t rescale_limit_val(uint32_t val) { return (val * (BACKLIGHT_LIMIT_VAL + 1)) / 256; } + // range for val is [0..TIMER_TOP]. PWM pin is high while the timer count is below val. static inline void set_pwm(uint16_t val) { OCRxx = val; } @@ -269,7 +277,7 @@ void backlight_set(uint8_t level) { #endif } // Set the brightness - set_pwm(cie_lightness(TIMER_TOP * (uint32_t)level / BACKLIGHT_LEVELS)); + set_pwm(cie_lightness(rescale_limit_val(TIMER_TOP * (uint32_t)level / BACKLIGHT_LEVELS))); } void backlight_task(void) {} @@ -375,7 +383,7 @@ ISR(TIMERx_OVF_vect) breathing_interrupt_disable(); } - set_pwm(cie_lightness(scale_backlight((uint16_t)pgm_read_byte(&breathing_table[index]) * 0x0101U))); + set_pwm(cie_lightness(rescale_limit_val(scale_backlight((uint16_t)pgm_read_byte(&breathing_table[index]) * 0x0101U)))); } #endif // BACKLIGHT_BREATHING diff --git a/quantum/backlight/backlight_chibios.c b/quantum/backlight/backlight_chibios.c index 0fe812bf27bc..4d5a69e14e33 100644 --- a/quantum/backlight/backlight_chibios.c +++ b/quantum/backlight/backlight_chibios.c @@ -3,6 +3,11 @@ #include #include "debug.h" +// Maximum duty cycle limit +#ifndef BACKLIGHT_LIMIT_VAL +# define BACKLIGHT_LIMIT_VAL 255 +#endif + // GPIOV2 && GPIOV3 #ifndef BACKLIGHT_PAL_MODE # define BACKLIGHT_PAL_MODE 2 @@ -58,6 +63,11 @@ static uint16_t cie_lightness(uint16_t v) { } } +static uint32_t rescale_limit_val(uint32_t val) { + // rescale the supplied backlight value to be in terms of the value limit + return (val * (BACKLIGHT_LIMIT_VAL + 1)) / 256; +} + void backlight_init_ports(void) { #ifdef USE_GPIOV1 palSetPadMode(PAL_PORT(BACKLIGHT_PIN), PAL_PAD(BACKLIGHT_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); @@ -85,7 +95,7 @@ void backlight_set(uint8_t level) { pwmDisableChannel(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1); } else { // Turn backlight on - uint32_t duty = (uint32_t)(cie_lightness(0xFFFF * (uint32_t)level / BACKLIGHT_LEVELS)); + uint32_t duty = (uint32_t)(cie_lightness(rescale_limit_val(0xFFFF * (uint32_t)level / BACKLIGHT_LEVELS))); pwmEnableChannel(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, duty)); } } @@ -129,7 +139,7 @@ void breathing_callback(PWMDriver *pwmp) { static uint16_t breathing_counter = 0; breathing_counter = (breathing_counter + 1) % (breathing_period * 256); uint8_t index = breathing_counter / interval % BREATHING_STEPS; - uint32_t duty = cie_lightness(scale_backlight(breathing_table[index] * 256)); + uint32_t duty = cie_lightness(rescale_limit_val(scale_backlight(breathing_table[index] * 256))); chSysLockFromISR(); pwmEnableChannelI(pwmp, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, duty)); diff --git a/quantum/config_common.h b/quantum/config_common.h index c1e6698e5053..2d9c70b08de6 100644 --- a/quantum/config_common.h +++ b/quantum/config_common.h @@ -39,7 +39,7 @@ # define PIND_ADDRESS 0x9 # define PINE_ADDRESS 0xC # define PINF_ADDRESS 0xF -# elif defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__) +# elif defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__) # define ADDRESS_BASE 0x00 # define PINB_ADDRESS 0x3 # define PINC_ADDRESS 0x6 @@ -58,11 +58,6 @@ # define PINC_ADDRESS 0x3 # define PINB_ADDRESS 0x6 # define PINA_ADDRESS 0x9 -# elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__) -# define ADDRESS_BASE 0x00 -# define PINB_ADDRESS 0x3 -# define PINC_ADDRESS 0x6 -# define PIND_ADDRESS 0x9 # elif defined(__AVR_ATtiny85__) # define ADDRESS_BASE 0x10 # define PINB_ADDRESS 0x6 @@ -284,6 +279,91 @@ # define F13 PAL_LINE(GPIOF, 13) # define F14 PAL_LINE(GPIOF, 14) # define F15 PAL_LINE(GPIOF, 15) +# define G0 PAL_LINE(GPIOG, 0) +# define G1 PAL_LINE(GPIOG, 1) +# define G2 PAL_LINE(GPIOG, 2) +# define G3 PAL_LINE(GPIOG, 3) +# define G4 PAL_LINE(GPIOG, 4) +# define G5 PAL_LINE(GPIOG, 5) +# define G6 PAL_LINE(GPIOG, 6) +# define G7 PAL_LINE(GPIOG, 7) +# define G8 PAL_LINE(GPIOG, 8) +# define G9 PAL_LINE(GPIOG, 9) +# define G10 PAL_LINE(GPIOG, 10) +# define G11 PAL_LINE(GPIOG, 11) +# define G12 PAL_LINE(GPIOG, 12) +# define G13 PAL_LINE(GPIOG, 13) +# define G14 PAL_LINE(GPIOG, 14) +# define G15 PAL_LINE(GPIOG, 15) +# define H0 PAL_LINE(GPIOH, 0) +# define H1 PAL_LINE(GPIOH, 1) +# define H2 PAL_LINE(GPIOH, 2) +# define H3 PAL_LINE(GPIOH, 3) +# define H4 PAL_LINE(GPIOH, 4) +# define H5 PAL_LINE(GPIOH, 5) +# define H6 PAL_LINE(GPIOH, 6) +# define H7 PAL_LINE(GPIOH, 7) +# define H8 PAL_LINE(GPIOH, 8) +# define H9 PAL_LINE(GPIOH, 9) +# define H10 PAL_LINE(GPIOH, 10) +# define H11 PAL_LINE(GPIOH, 11) +# define H12 PAL_LINE(GPIOH, 12) +# define H13 PAL_LINE(GPIOH, 13) +# define H14 PAL_LINE(GPIOH, 14) +# define H15 PAL_LINE(GPIOH, 15) +# define I0 PAL_LINE(GPIOI, 0) +# define I1 PAL_LINE(GPIOI, 1) +# define I2 PAL_LINE(GPIOI, 2) +# define I3 PAL_LINE(GPIOI, 3) +# define I4 PAL_LINE(GPIOI, 4) +# define I5 PAL_LINE(GPIOI, 5) +# define I6 PAL_LINE(GPIOI, 6) +# define I7 PAL_LINE(GPIOI, 7) +# define I8 PAL_LINE(GPIOI, 8) +# define I9 PAL_LINE(GPIOI, 9) +# define I10 PAL_LINE(GPIOI, 10) +# define I11 PAL_LINE(GPIOI, 11) +# define I12 PAL_LINE(GPIOI, 12) +# define I13 PAL_LINE(GPIOI, 13) +# define I14 PAL_LINE(GPIOI, 14) +# define I15 PAL_LINE(GPIOI, 15) +# define J0 PAL_LINE(GPIOJ, 0) +# define J1 PAL_LINE(GPIOJ, 1) +# define J2 PAL_LINE(GPIOJ, 2) +# define J3 PAL_LINE(GPIOJ, 3) +# define J4 PAL_LINE(GPIOJ, 4) +# define J5 PAL_LINE(GPIOJ, 5) +# define J6 PAL_LINE(GPIOJ, 6) +# define J7 PAL_LINE(GPIOJ, 7) +# define J8 PAL_LINE(GPIOJ, 8) +# define J9 PAL_LINE(GPIOJ, 9) +# define J10 PAL_LINE(GPIOJ, 10) +# define J11 PAL_LINE(GPIOJ, 11) +# define J12 PAL_LINE(GPIOJ, 12) +# define J13 PAL_LINE(GPIOJ, 13) +# define J14 PAL_LINE(GPIOJ, 14) +# define J15 PAL_LINE(GPIOJ, 15) +// Keyboards can `#define KEYBOARD_REQUIRES_GPIOK` if they need to access GPIO-K pins. These conflict with a whole +// bunch of layout definitions, so it's intentionally left out unless absolutely required -- in that case, the +// keyboard designer should use a different symbol when defining their layout macros. +# ifdef KEYBOARD_REQUIRES_GPIOK +# define K0 PAL_LINE(GPIOK, 0) +# define K1 PAL_LINE(GPIOK, 1) +# define K2 PAL_LINE(GPIOK, 2) +# define K3 PAL_LINE(GPIOK, 3) +# define K4 PAL_LINE(GPIOK, 4) +# define K5 PAL_LINE(GPIOK, 5) +# define K6 PAL_LINE(GPIOK, 6) +# define K7 PAL_LINE(GPIOK, 7) +# define K8 PAL_LINE(GPIOK, 8) +# define K9 PAL_LINE(GPIOK, 9) +# define K10 PAL_LINE(GPIOK, 10) +# define K11 PAL_LINE(GPIOK, 11) +# define K12 PAL_LINE(GPIOK, 12) +# define K13 PAL_LINE(GPIOK, 13) +# define K14 PAL_LINE(GPIOK, 14) +# define K15 PAL_LINE(GPIOK, 15) +# endif # endif #endif diff --git a/quantum/encoder.c b/quantum/encoder.c index 81ec1bb376c8..7ca31afedc2f 100644 --- a/quantum/encoder.c +++ b/quantum/encoder.c @@ -23,7 +23,7 @@ // for memcpy #include -#ifndef ENCODER_RESOLUTION +#if !defined(ENCODER_RESOLUTIONS) && !defined(ENCODER_RESOLUTION) # define ENCODER_RESOLUTION 4 #endif @@ -34,6 +34,9 @@ #define NUMBER_OF_ENCODERS (sizeof(encoders_pad_a) / sizeof(pin_t)) static pin_t encoders_pad_a[] = ENCODERS_PAD_A; static pin_t encoders_pad_b[] = ENCODERS_PAD_B; +#ifdef ENCODER_RESOLUTIONS +static uint8_t encoder_resolutions[] = ENCODER_RESOLUTIONS; +#endif #ifndef ENCODER_DIRECTION_FLIP # define ENCODER_CLOCKWISE true @@ -65,9 +68,15 @@ void encoder_init(void) { if (!isLeftHand) { const pin_t encoders_pad_a_right[] = ENCODERS_PAD_A_RIGHT; const pin_t encoders_pad_b_right[] = ENCODERS_PAD_B_RIGHT; +# if defined(ENCODER_RESOLUTIONS_RIGHT) + const uint8_t encoder_resolutions_right[] = ENCODER_RESOLUTIONS_RIGHT; +# endif for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) { encoders_pad_a[i] = encoders_pad_a_right[i]; encoders_pad_b[i] = encoders_pad_b_right[i]; +# if defined(ENCODER_RESOLUTIONS_RIGHT) + encoder_resolutions[i] = encoder_resolutions_right[i]; +# endif } } #endif @@ -87,19 +96,26 @@ void encoder_init(void) { static void encoder_update(int8_t index, uint8_t state) { uint8_t i = index; + +#ifdef ENCODER_RESOLUTIONS + int8_t resolution = encoder_resolutions[i]; +#else + int8_t resolution = ENCODER_RESOLUTION; +#endif + #ifdef SPLIT_KEYBOARD index += thisHand; #endif encoder_pulses[i] += encoder_LUT[state & 0xF]; - if (encoder_pulses[i] >= ENCODER_RESOLUTION) { + if (encoder_pulses[i] >= resolution) { encoder_value[index]++; encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE); } - if (encoder_pulses[i] <= -ENCODER_RESOLUTION) { // direction is arbitrary here, but this clockwise + if (encoder_pulses[i] <= -resolution) { // direction is arbitrary here, but this clockwise encoder_value[index]--; encoder_update_kb(index, ENCODER_CLOCKWISE); } - encoder_pulses[i] %= ENCODER_RESOLUTION; + encoder_pulses[i] %= resolution; } void encoder_read(void) { diff --git a/quantum/joystick.h b/quantum/joystick.h index a95472b9fd44..87dbc24aff81 100644 --- a/quantum/joystick.h +++ b/quantum/joystick.h @@ -1,5 +1,9 @@ #pragma once +#include "quantum.h" + +#include + #ifndef JOYSTICK_BUTTON_COUNT # define JOYSTICK_BUTTON_COUNT 8 #endif @@ -8,9 +12,13 @@ # define JOYSTICK_AXES_COUNT 4 #endif -#include "quantum.h" +#ifndef JOYSTICK_AXES_RESOLUTION +# define JOYSTICK_AXES_RESOLUTION 8 +#elif JOYSTICK_AXES_RESOLUTION < 8 || JOYSTICK_AXES_RESOLUTION > 16 +# error JOYSTICK_AXES_RESOLUTION must be between 8 and 16 +#endif -#include +#define JOYSTICK_RESOLUTION ((1L << (JOYSTICK_AXES_RESOLUTION - 1)) - 1) // configure on input_pin of the joystick_axes array entry to JS_VIRTUAL_AXIS // to prevent it from being read from the ADC. This allows outputing forged axis value. diff --git a/quantum/led_matrix.c b/quantum/led_matrix.c index 5c24c797a91f..eb523990a6c1 100644 --- a/quantum/led_matrix.c +++ b/quantum/led_matrix.c @@ -20,7 +20,7 @@ #include #include #include "quantum.h" -#include "ledmatrix.h" +#include "led_matrix.h" #include "progmem.h" #include "config.h" #include "eeprom.h" diff --git a/quantum/ledmatrix.h b/quantum/led_matrix.h similarity index 100% rename from quantum/ledmatrix.h rename to quantum/led_matrix.h diff --git a/quantum/led_matrix_drivers.c b/quantum/led_matrix_drivers.c index 6877bf4c6bbb..9decaa33c2f2 100644 --- a/quantum/led_matrix_drivers.c +++ b/quantum/led_matrix_drivers.c @@ -18,7 +18,7 @@ #include #include #include "quantum.h" -#include "ledmatrix.h" +#include "led_matrix.h" /* Each driver needs to define a struct: * diff --git a/quantum/matrix.c b/quantum/matrix.c index c68c56cac204..cab0d2ddcafa 100644 --- a/quantum/matrix.c +++ b/quantum/matrix.c @@ -32,6 +32,19 @@ static const pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS; extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values +static inline void setPinOutput_writeLow(pin_t pin) { + ATOMIC_BLOCK_FORCEON { + setPinOutput(pin); + writePinLow(pin); + } +} + +static inline void setPinInputHigh_atomic(pin_t pin) { + ATOMIC_BLOCK_FORCEON { + setPinInputHigh(pin); + } +} + // matrix code #ifdef DIRECT_PINS @@ -70,22 +83,23 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) # if (DIODE_DIRECTION == COL2ROW) static void select_row(uint8_t row) { - setPinOutput(row_pins[row]); - writePinLow(row_pins[row]); + setPinOutput_writeLow(row_pins[row]); } -static void unselect_row(uint8_t row) { setPinInputHigh(row_pins[row]); } +static void unselect_row(uint8_t row) { + setPinInputHigh_atomic(row_pins[row]); +} static void unselect_rows(void) { for (uint8_t x = 0; x < MATRIX_ROWS; x++) { - setPinInputHigh(row_pins[x]); + setPinInputHigh_atomic(row_pins[x]); } } static void init_pins(void) { unselect_rows(); for (uint8_t x = 0; x < MATRIX_COLS; x++) { - setPinInputHigh(col_pins[x]); + setPinInputHigh_atomic(col_pins[x]); } } @@ -120,22 +134,23 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) # elif (DIODE_DIRECTION == ROW2COL) static void select_col(uint8_t col) { - setPinOutput(col_pins[col]); - writePinLow(col_pins[col]); + setPinOutput_writeLow(col_pins[col]); } -static void unselect_col(uint8_t col) { setPinInputHigh(col_pins[col]); } +static void unselect_col(uint8_t col) { + setPinInputHigh_atomic(col_pins[col]); +} static void unselect_cols(void) { for (uint8_t x = 0; x < MATRIX_COLS; x++) { - setPinInputHigh(col_pins[x]); + setPinInputHigh_atomic(col_pins[x]); } } static void init_pins(void) { unselect_cols(); for (uint8_t x = 0; x < MATRIX_ROWS; x++) { - setPinInputHigh(row_pins[x]); + setPinInputHigh_atomic(row_pins[x]); } } diff --git a/quantum/mcu_selection.mk b/quantum/mcu_selection.mk index 295dfd3189e5..9518a6463f54 100644 --- a/quantum/mcu_selection.mk +++ b/quantum/mcu_selection.mk @@ -318,6 +318,9 @@ ifneq (,$(filter $(MCU),atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 a ifeq (,$(filter $(NO_INTERRUPT_CONTROL_ENDPOINT),yes)) OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT endif + ifneq (,$(filter $(MCU),atmega16u2 atmega32u2)) + NO_I2C = yes + endif endif ifneq (,$(filter $(MCU),atmega32a)) diff --git a/quantum/process_keycode/process_auto_shift.c b/quantum/process_keycode/process_auto_shift.c index b1267922ce77..a2d315408b09 100644 --- a/quantum/process_keycode/process_auto_shift.c +++ b/quantum/process_keycode/process_auto_shift.c @@ -16,48 +16,149 @@ #ifdef AUTO_SHIFT_ENABLE +# include # include # include "process_auto_shift.h" -static bool autoshift_enabled = true; static uint16_t autoshift_time = 0; static uint16_t autoshift_timeout = AUTO_SHIFT_TIMEOUT; static uint16_t autoshift_lastkey = KC_NO; +static struct { + // Whether autoshift is enabled. + bool enabled : 1; + // Whether the last auto-shifted key was released after the timeout. This + // is used to replicate the last key for a tap-then-hold. + bool lastshifted : 1; + // Whether an auto-shiftable key has been pressed but not processed. + bool in_progress : 1; + // Whether the auto-shifted keypress has been registered. + bool holding_shift : 1; +} autoshift_flags = {true, false, false, false}; + +/** \brief Record the press of an autoshiftable key + * + * \return Whether the record should be further processed. + */ +static bool autoshift_press(uint16_t keycode, uint16_t now, keyrecord_t *record) { + if (!autoshift_flags.enabled) { + return true; + } + +# ifndef AUTO_SHIFT_MODIFIERS + if (get_mods() & (~MOD_BIT(KC_LSFT))) { + return true; + } +# endif +# ifdef AUTO_SHIFT_REPEAT + const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time); +# ifndef AUTO_SHIFT_NO_AUTO_REPEAT + if (!autoshift_flags.lastshifted) { +# endif + if (elapsed < TAPPING_TERM && keycode == autoshift_lastkey) { + // Allow a tap-then-hold for keyrepeat. + if (!autoshift_flags.lastshifted) { + register_code(autoshift_lastkey); + } else { + // Simulate pressing the shift key. + add_weak_mods(MOD_BIT(KC_LSFT)); + register_code(autoshift_lastkey); + } + return false; + } +# ifndef AUTO_SHIFT_NO_AUTO_REPEAT + } +# endif +# endif -void autoshift_flush(void) { - if (autoshift_lastkey != KC_NO) { - uint16_t elapsed = timer_elapsed(autoshift_time); + // Record the keycode so we can simulate it later. + autoshift_lastkey = keycode; + autoshift_time = now; + autoshift_flags.in_progress = true; - if (elapsed > autoshift_timeout) { - tap_code16(LSFT(autoshift_lastkey)); +# if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING) + clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); +# endif + return false; +} + +/** \brief Registers an autoshiftable key under the right conditions + * + * If the autoshift delay has elapsed, register a shift and the key. + * + * If the autoshift key is released before the delay has elapsed, register the + * key without a shift. + */ +static void autoshift_end(uint16_t keycode, uint16_t now, bool matrix_trigger) { + // Called on key down with KC_NO, auto-shifted key up, and timeout. + if (autoshift_flags.in_progress) { + // Process the auto-shiftable key. + autoshift_flags.in_progress = false; + + // Time since the initial press was recorded. + const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time); + if (elapsed < autoshift_timeout) { + register_code(autoshift_lastkey); + autoshift_flags.lastshifted = false; } else { - tap_code(autoshift_lastkey); + // Simulate pressing the shift key. + add_weak_mods(MOD_BIT(KC_LSFT)); + register_code(autoshift_lastkey); + autoshift_flags.lastshifted = true; +# if defined(AUTO_SHIFT_REPEAT) && !defined(AUTO_SHIFT_NO_AUTO_REPEAT) + if (matrix_trigger) { + // Prevents release. + return; + } +# endif } - autoshift_time = 0; - autoshift_lastkey = KC_NO; +# if TAP_CODE_DELAY > 0 + wait_ms(TAP_CODE_DELAY); +# endif + unregister_code(autoshift_lastkey); + del_weak_mods(MOD_BIT(KC_LSFT)); + } else { + // Release after keyrepeat. + unregister_code(keycode); + if (keycode == autoshift_lastkey) { + // This will only fire when the key was the last auto-shiftable + // pressed. That prevents aaaaBBBB then releasing a from unshifting + // later Bs (if B wasn't auto-shiftable). + del_weak_mods(MOD_BIT(KC_LSFT)); + } } + send_keyboard_report(); // del_weak_mods doesn't send one. + // Roll the autoshift_time forward for detecting tap-and-hold. + autoshift_time = now; } -void autoshift_on(uint16_t keycode) { - autoshift_time = timer_read(); - autoshift_lastkey = keycode; +/** \brief Simulates auto-shifted key releases when timeout is hit + * + * Can be called from \c matrix_scan_user so that auto-shifted keys are sent + * immediately after the timeout has expired, rather than waiting for the key + * to be released. + */ +void autoshift_matrix_scan(void) { + if (autoshift_flags.in_progress) { + const uint16_t now = timer_read(); + const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time); + if (elapsed >= autoshift_timeout) { + autoshift_end(autoshift_lastkey, now, true); + } + } } void autoshift_toggle(void) { - if (autoshift_enabled) { - autoshift_enabled = false; - autoshift_flush(); - } else { - autoshift_enabled = true; - } + autoshift_flags.enabled = !autoshift_flags.enabled; + del_weak_mods(MOD_BIT(KC_LSFT)); } -void autoshift_enable(void) { autoshift_enabled = true; } +void autoshift_enable(void) { autoshift_flags.enabled = true; } + void autoshift_disable(void) { - autoshift_enabled = false; - autoshift_flush(); + autoshift_flags.enabled = false; + del_weak_mods(MOD_BIT(KC_LSFT)); } # ifndef AUTO_SHIFT_NO_SETUP @@ -70,19 +171,30 @@ void autoshift_timer_report(void) { } # endif -bool get_autoshift_state(void) { return autoshift_enabled; } +bool get_autoshift_state(void) { return autoshift_flags.enabled; } uint16_t get_autoshift_timeout(void) { return autoshift_timeout; } void set_autoshift_timeout(uint16_t timeout) { autoshift_timeout = timeout; } bool process_auto_shift(uint16_t keycode, keyrecord_t *record) { + // Note that record->event.time isn't reliable, see: + // https://github.com/qmk/qmk_firmware/pull/9826#issuecomment-733559550 + const uint16_t now = timer_read(); + if (record->event.pressed) { + if (autoshift_flags.in_progress) { + // Evaluate previous key if there is one. Doing this elsewhere is + // more complicated and easier to break. + autoshift_end(KC_NO, now, false); + } + // For pressing another key while keyrepeating shifted autoshift. + del_weak_mods(MOD_BIT(KC_LSFT)); + switch (keycode) { case KC_ASTG: autoshift_toggle(); return true; - case KC_ASON: autoshift_enable(); return true; @@ -102,41 +214,28 @@ bool process_auto_shift(uint16_t keycode, keyrecord_t *record) { autoshift_timer_report(); return true; # endif + } + } + + switch (keycode) { # ifndef NO_AUTO_SHIFT_ALPHA - case KC_A ... KC_Z: + case KC_A ... KC_Z: # endif # ifndef NO_AUTO_SHIFT_NUMERIC - case KC_1 ... KC_0: + case KC_1 ... KC_0: # endif # ifndef NO_AUTO_SHIFT_SPECIAL - case KC_TAB: - case KC_MINUS ... KC_SLASH: - case KC_NONUS_BSLASH: -# endif - autoshift_flush(); - if (!autoshift_enabled) return true; - -# ifndef AUTO_SHIFT_MODIFIERS - if (get_mods()) { - return true; - } -# endif - autoshift_on(keycode); - - // We need some extra handling here for OSL edge cases -# if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING) - clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); + case KC_TAB: + case KC_MINUS ... KC_SLASH: + case KC_NONUS_BSLASH: # endif + if (record->event.pressed) { + return autoshift_press(keycode, now, record); + } else { + autoshift_end(keycode, now, false); return false; - - default: - autoshift_flush(); - return true; - } - } else { - autoshift_flush(); + } } - return true; } diff --git a/quantum/process_keycode/process_auto_shift.h b/quantum/process_keycode/process_auto_shift.h index e86c4658e98d..5b2718f11ca6 100644 --- a/quantum/process_keycode/process_auto_shift.h +++ b/quantum/process_keycode/process_auto_shift.h @@ -30,3 +30,4 @@ void autoshift_toggle(void); bool get_autoshift_state(void); uint16_t get_autoshift_timeout(void); void set_autoshift_timeout(uint16_t timeout); +void autoshift_matrix_scan(void); diff --git a/quantum/process_keycode/process_joystick.c b/quantum/process_keycode/process_joystick.c index 5778a7434c1c..3ffaf42bf847 100644 --- a/quantum/process_keycode/process_joystick.c +++ b/quantum/process_keycode/process_joystick.c @@ -129,17 +129,17 @@ bool process_joystick_analogread_quantum() { // test the converted value against the lower range int32_t ref = joystick_axes[axis_index].mid_digit; int32_t range = joystick_axes[axis_index].min_digit; - int32_t ranged_val = ((axis_val - ref) * -127) / (range - ref); + int32_t ranged_val = ((axis_val - ref) * -JOYSTICK_RESOLUTION) / (range - ref); if (ranged_val > 0) { // the value is in the higher range range = joystick_axes[axis_index].max_digit; - ranged_val = ((axis_val - ref) * 127) / (range - ref); + ranged_val = ((axis_val - ref) * JOYSTICK_RESOLUTION) / (range - ref); } // clamp the result in the valid range - ranged_val = ranged_val < -127 ? -127 : ranged_val; - ranged_val = ranged_val > 127 ? 127 : ranged_val; + ranged_val = ranged_val < -JOYSTICK_RESOLUTION ? -JOYSTICK_RESOLUTION : ranged_val; + ranged_val = ranged_val > JOYSTICK_RESOLUTION ? JOYSTICK_RESOLUTION : ranged_val; if (ranged_val != joystick_status.axes[axis_index]) { joystick_status.axes[axis_index] = ranged_val; diff --git a/quantum/process_keycode/process_midi.c b/quantum/process_keycode/process_midi.c index e52577014432..8e2fb955e7e3 100644 --- a/quantum/process_keycode/process_midi.c +++ b/quantum/process_keycode/process_midi.c @@ -41,12 +41,12 @@ static int8_t midi_modulation_step; static uint16_t midi_modulation_timer; midi_config_t midi_config; -inline uint8_t compute_velocity(uint8_t setting) { return (setting + 1) * (128 / (MIDI_VELOCITY_MAX - MIDI_VELOCITY_MIN + 1)); } +inline uint8_t compute_velocity(uint8_t setting) { return setting * (128 / (MIDI_VELOCITY_MAX - MIDI_VELOCITY_MIN)); } void midi_init(void) { midi_config.octave = MI_OCT_2 - MIDI_OCTAVE_MIN; midi_config.transpose = 0; - midi_config.velocity = (MIDI_VELOCITY_MAX - MIDI_VELOCITY_MIN); + midi_config.velocity = 127; midi_config.channel = 0; midi_config.modulation_interval = 8; @@ -66,7 +66,7 @@ bool process_midi(uint16_t keycode, keyrecord_t *record) { case MIDI_TONE_MIN ... MIDI_TONE_MAX: { uint8_t channel = midi_config.channel; uint8_t tone = keycode - MIDI_TONE_MIN; - uint8_t velocity = compute_velocity(midi_config.velocity); + uint8_t velocity = midi_config.velocity; if (record->event.pressed) { if (tone_status[tone] == MIDI_INVALID_NOTE) { uint8_t note = midi_compute_note(keycode); @@ -124,19 +124,30 @@ bool process_midi(uint16_t keycode, keyrecord_t *record) { return false; case MIDI_VELOCITY_MIN ... MIDI_VELOCITY_MAX: if (record->event.pressed) { - midi_config.velocity = keycode - MIDI_VELOCITY_MIN; + midi_config.velocity = compute_velocity(keycode - MIDI_VELOCITY_MIN); dprintf("midi velocity %d\n", midi_config.velocity); } return false; case MI_VELD: if (record->event.pressed && midi_config.velocity > 0) { - midi_config.velocity--; + if (midi_config.velocity == 127) { + midi_config.velocity -= 10; + } else if (midi_config.velocity > 12) { + midi_config.velocity -= 13; + } else { + midi_config.velocity = 0; + } + dprintf("midi velocity %d\n", midi_config.velocity); } return false; case MI_VELU: - if (record->event.pressed) { - midi_config.velocity++; + if (record->event.pressed && midi_config.velocity < 127) { + if (midi_config.velocity < 115) { + midi_config.velocity += 13; + } else { + midi_config.velocity = 127; + } dprintf("midi velocity %d\n", midi_config.velocity); } return false; diff --git a/quantum/process_keycode/process_midi.h b/quantum/process_keycode/process_midi.h index 0007b3ed255a..ef5661dd4d1c 100644 --- a/quantum/process_keycode/process_midi.h +++ b/quantum/process_keycode/process_midi.h @@ -35,7 +35,7 @@ typedef union { struct { uint8_t octave : 4; int8_t transpose : 4; - uint8_t velocity : 4; + uint8_t velocity : 7; uint8_t channel : 4; uint8_t modulation_interval : 4; }; diff --git a/quantum/process_keycode/process_sequencer.c b/quantum/process_keycode/process_sequencer.c new file mode 100644 index 000000000000..334b4c00923f --- /dev/null +++ b/quantum/process_keycode/process_sequencer.c @@ -0,0 +1,62 @@ +/* Copyright 2020 Rodolphe Belouin + * + * 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 . + */ + +#include "process_sequencer.h" + +bool process_sequencer(uint16_t keycode, keyrecord_t *record) { + if (record->event.pressed) { + switch (keycode) { + case SQ_ON: + sequencer_on(); + return false; + case SQ_OFF: + sequencer_off(); + return false; + case SQ_TOG: + sequencer_toggle(); + return false; + case SQ_TMPD: + sequencer_decrease_tempo(); + return false; + case SQ_TMPU: + sequencer_increase_tempo(); + return false; + case SEQUENCER_RESOLUTION_MIN ... SEQUENCER_RESOLUTION_MAX: + sequencer_set_resolution(keycode - SEQUENCER_RESOLUTION_MIN); + return false; + case SQ_RESD: + sequencer_decrease_resolution(); + return false; + case SQ_RESU: + sequencer_increase_resolution(); + return false; + case SQ_SALL: + sequencer_set_all_steps_on(); + return false; + case SQ_SCLR: + sequencer_set_all_steps_off(); + return false; + case SEQUENCER_STEP_MIN ... SEQUENCER_STEP_MAX: + sequencer_toggle_step(keycode - SEQUENCER_STEP_MIN); + return false; + case SEQUENCER_TRACK_MIN ... SEQUENCER_TRACK_MAX: + sequencer_toggle_single_active_track(keycode - SEQUENCER_TRACK_MIN); + return false; + } + } + + return true; +} diff --git a/quantum/process_keycode/process_sequencer.h b/quantum/process_keycode/process_sequencer.h new file mode 100644 index 000000000000..2b85f24299ed --- /dev/null +++ b/quantum/process_keycode/process_sequencer.h @@ -0,0 +1,21 @@ +/* Copyright 2020 Rodolphe Belouin + * + * 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 . + */ + +#pragma once + +#include "quantum.h" + +bool process_sequencer(uint16_t keycode, keyrecord_t *record); diff --git a/quantum/quantum.c b/quantum/quantum.c index 0b2f98762dd8..3ac0ed87161a 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -58,6 +58,10 @@ float bell_song[][2] = SONG(TERMINAL_SOUND); # endif #endif +#ifdef AUTO_SHIFT_ENABLE +# include "process_auto_shift.h" +#endif + static void do_code16(uint16_t code, void (*f)(uint8_t)) { switch (code) { case QK_MODS ... QK_MODS_MAX: @@ -228,6 +232,9 @@ bool process_record_quantum(keyrecord_t *record) { process_record_via(keycode, record) && #endif process_record_kb(keycode, record) && +#if defined(SEQUENCER_ENABLE) + process_sequencer(keycode, record) && +#endif #if defined(MIDI_ENABLE) && defined(MIDI_ADVANCED) process_midi(keycode, record) && #endif @@ -636,6 +643,10 @@ void matrix_scan_quantum() { matrix_scan_music(); #endif +#ifdef SEQUENCER_ENABLE + matrix_scan_sequencer(); +#endif + #ifdef TAP_DANCE_ENABLE matrix_scan_tap_dance(); #endif @@ -664,6 +675,10 @@ void matrix_scan_quantum() { dip_switch_read(false); #endif +#ifdef AUTO_SHIFT_ENABLE + autoshift_matrix_scan(); +#endif + matrix_scan_kb(); } diff --git a/quantum/quantum.h b/quantum/quantum.h index 0e452a062d61..cb0af306ac93 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -31,7 +31,7 @@ #ifdef BACKLIGHT_ENABLE # ifdef LED_MATRIX_ENABLE -# include "ledmatrix.h" +# include "led_matrix.h" # else # include "backlight.h" # endif @@ -68,6 +68,11 @@ extern layer_state_t default_layer_state; extern layer_state_t layer_state; #endif +#if defined(SEQUENCER_ENABLE) +# include "sequencer.h" +# include "process_sequencer.h" +#endif + #if defined(MIDI_ENABLE) && defined(MIDI_ADVANCED) # include "process_midi.h" #endif @@ -220,6 +225,61 @@ typedef ioline_t pin_t; # define togglePin(pin) palToggleLine(pin) #endif +// Atomic macro to help make GPIO and other controls atomic. +#ifdef IGNORE_ATOMIC_BLOCK +/* do nothing atomic macro */ +# define ATOMIC_BLOCK for (uint8_t __ToDo = 1; __ToDo; __ToDo = 0) +# define ATOMIC_BLOCK_RESTORESTATE ATOMIC_BLOCK +# define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK + +#elif defined(__AVR__) +/* atomic macro for AVR */ +# include + +# define ATOMIC_BLOCK_RESTORESTATE ATOMIC_BLOCK(ATOMIC_RESTORESTATE) +# define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK(ATOMIC_FORCEON) + +#elif defined(PROTOCOL_CHIBIOS) || defined(PROTOCOL_ARM_ATSAM) +/* atomic macro for ChibiOS / ARM ATSAM */ +# if defined(PROTOCOL_ARM_ATSAM) +# include "arm_atsam_protocol.h" +# endif + +static __inline__ uint8_t __interrupt_disable__(void) { +# if defined(PROTOCOL_CHIBIOS) + chSysLock(); +# endif +# if defined(PROTOCOL_ARM_ATSAM) + __disable_irq(); +# endif + return 1; +} + +static __inline__ void __interrupt_enable__(const uint8_t *__s) { +# if defined(PROTOCOL_CHIBIOS) + chSysUnlock(); +# endif +# if defined(PROTOCOL_ARM_ATSAM) + __enable_irq(); +# endif + __asm__ volatile("" ::: "memory"); + (void)__s; +} + +# define ATOMIC_BLOCK(type) for (type, __ToDo = __interrupt_disable__(); __ToDo; __ToDo = 0) +# define ATOMIC_FORCEON uint8_t sreg_save __attribute__((__cleanup__(__interrupt_enable__))) = 0 + +# define ATOMIC_BLOCK_RESTORESTATE _Static_assert(0, "ATOMIC_BLOCK_RESTORESTATE dose not implement") +# define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK(ATOMIC_FORCEON) + +/* Other platform */ +#else + +# define ATOMIC_BLOCK_RESTORESTATE _Static_assert(0, "ATOMIC_BLOCK_RESTORESTATE dose not implement") +# define ATOMIC_BLOCK_FORCEON _Static_assert(0, "ATOMIC_BLOCK_FORCEON dose not implement") + +#endif + #define SEND_STRING(string) send_string_P(PSTR(string)) #define SEND_STRING_DELAY(string, interval) send_string_with_delay_P(PSTR(string), interval) diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index a0a7bc340f23..a2cc7b38d91b 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -16,6 +16,10 @@ #ifndef QUANTUM_KEYCODES_H #define QUANTUM_KEYCODES_H +#if defined(SEQUENCER_ENABLE) +# include "sequencer.h" +#endif + #ifndef MIDI_ENABLE_STRICT # define MIDI_ENABLE_STRICT 0 #endif @@ -343,7 +347,8 @@ enum quantum_keycodes { MI_TRNSU, // transpose up MIDI_VELOCITY_MIN, - MI_VEL_1 = MIDI_VELOCITY_MIN, + MI_VEL_0 = MIDI_VELOCITY_MIN, + MI_VEL_1, MI_VEL_2, MI_VEL_3, MI_VEL_4, @@ -549,6 +554,37 @@ enum quantum_keycodes { JS_BUTTON31, JS_BUTTON_MAX = JS_BUTTON31, +#if defined(SEQUENCER_ENABLE) + SQ_ON, + SQ_OFF, + SQ_TOG, + + SQ_TMPD, // Decrease tempo + SQ_TMPU, // Increase tempo + + SEQUENCER_RESOLUTION_MIN, + SEQUENCER_RESOLUTION_MAX = SEQUENCER_RESOLUTION_MIN + SEQUENCER_RESOLUTIONS, + SQ_RESD, // Decrease resolution + SQ_RESU, // Increase resolution + + SQ_SALL, // All steps on + SQ_SCLR, // All steps off + SEQUENCER_STEP_MIN, + SEQUENCER_STEP_MAX = SEQUENCER_STEP_MIN + SEQUENCER_STEPS, + + SEQUENCER_TRACK_MIN, + SEQUENCER_TRACK_MAX = SEQUENCER_TRACK_MIN + SEQUENCER_TRACKS, + +/** + * Helpers to assign a keycode to a step, a resolution, or a track. + * Falls back to NOOP if n is out of range. + */ +# define SQ_S(n) (n < SEQUENCER_STEPS ? SEQUENCER_STEP_MIN + n : XXXXXXX) +# define SQ_R(n) (n < SEQUENCER_RESOLUTIONS ? SEQUENCER_RESOLUTION_MIN + n : XXXXXXX) +# define SQ_T(n) (n < SEQUENCER_TRACKS ? SEQUENCER_TRACK_MIN + n : XXXXXXX) + +#endif + // always leave at the end SAFE_RANGE }; diff --git a/quantum/rgb_matrix.c b/quantum/rgb_matrix.c index 802c5afcee21..f239bd582f0e 100644 --- a/quantum/rgb_matrix.c +++ b/quantum/rgb_matrix.c @@ -31,6 +31,8 @@ const point_t k_rgb_matrix_center = {112, 32}; const point_t k_rgb_matrix_center = RGB_MATRIX_CENTER; #endif +__attribute__((weak)) RGB rgb_matrix_hsv_to_rgb(HSV hsv) { return hsv_to_rgb(hsv); } + // Generic effect runners #include "rgb_matrix_runners/effect_runner_dx_dy_dist.h" #include "rgb_matrix_runners/effect_runner_dx_dy.h" @@ -401,6 +403,10 @@ void rgb_matrix_task(void) { break; case RENDERING: rgb_task_render(effect); + if (!suspend_backlight) { + rgb_matrix_indicators(); + rgb_matrix_indicators_advanced(&rgb_effect_params); + } break; case FLUSHING: rgb_task_flush(effect); @@ -409,10 +415,6 @@ void rgb_matrix_task(void) { rgb_task_sync(); break; } - - if (!suspend_backlight) { - rgb_matrix_indicators(); - } } void rgb_matrix_indicators(void) { @@ -424,6 +426,28 @@ __attribute__((weak)) void rgb_matrix_indicators_kb(void) {} __attribute__((weak)) void rgb_matrix_indicators_user(void) {} +void rgb_matrix_indicators_advanced(effect_params_t *params) { + /* special handling is needed for "params->iter", since it's already been incremented. + * Could move the invocations to rgb_task_render, but then it's missing a few checks + * and not sure which would be better. Otherwise, this should be called from + * rgb_task_render, right before the iter++ line. + */ +#if defined(RGB_MATRIX_LED_PROCESS_LIMIT) && RGB_MATRIX_LED_PROCESS_LIMIT > 0 && RGB_MATRIX_LED_PROCESS_LIMIT < DRIVER_LED_TOTAL + uint8_t min = RGB_MATRIX_LED_PROCESS_LIMIT * (params->iter - 1); + uint8_t max = min + RGB_MATRIX_LED_PROCESS_LIMIT; + if (max > DRIVER_LED_TOTAL) max = DRIVER_LED_TOTAL; +#else + uint8_t min = 0; + uint8_t max = DRIVER_LED_TOTAL; +#endif + rgb_matrix_indicators_advanced_kb(min, max); + rgb_matrix_indicators_advanced_user(min, max); +} + +__attribute__((weak)) void rgb_matrix_indicators_advanced_kb(uint8_t led_min, uint8_t led_max) {} + +__attribute__((weak)) void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {} + void rgb_matrix_init(void) { rgb_matrix_driver.init(); diff --git a/quantum/rgb_matrix.h b/quantum/rgb_matrix.h index 733333349faa..771a1fcd35f6 100644 --- a/quantum/rgb_matrix.h +++ b/quantum/rgb_matrix.h @@ -57,6 +57,11 @@ uint8_t max = DRIVER_LED_TOTAL; #endif +#define RGB_MATRIX_INDICATOR_SET_COLOR(i, r, g, b) \ + if (i >= led_min && i <= led_max) { \ + rgb_matrix_set_color(i, r, g, b); \ + } + #define RGB_MATRIX_TEST_LED_FLAGS() \ if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) continue @@ -103,6 +108,10 @@ void rgb_matrix_indicators(void); void rgb_matrix_indicators_kb(void); void rgb_matrix_indicators_user(void); +void rgb_matrix_indicators_advanced(effect_params_t *params); +void rgb_matrix_indicators_advanced_kb(uint8_t led_min, uint8_t led_max); +void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max); + void rgb_matrix_init(void); void rgb_matrix_set_suspend_state(bool state); diff --git a/quantum/rgb_matrix_animations/alpha_mods_anim.h b/quantum/rgb_matrix_animations/alpha_mods_anim.h index 0778ab2098f4..426d88ef3569 100644 --- a/quantum/rgb_matrix_animations/alpha_mods_anim.h +++ b/quantum/rgb_matrix_animations/alpha_mods_anim.h @@ -7,9 +7,9 @@ bool ALPHAS_MODS(effect_params_t* params) { RGB_MATRIX_USE_LIMITS(led_min, led_max); HSV hsv = rgb_matrix_config.hsv; - RGB rgb1 = hsv_to_rgb(hsv); + RGB rgb1 = rgb_matrix_hsv_to_rgb(hsv); hsv.h += rgb_matrix_config.speed; - RGB rgb2 = hsv_to_rgb(hsv); + RGB rgb2 = rgb_matrix_hsv_to_rgb(hsv); for (uint8_t i = led_min; i < led_max; i++) { RGB_MATRIX_TEST_LED_FLAGS(); diff --git a/quantum/rgb_matrix_animations/breathing_anim.h b/quantum/rgb_matrix_animations/breathing_anim.h index 887425f9dafc..340bd93e5dd0 100644 --- a/quantum/rgb_matrix_animations/breathing_anim.h +++ b/quantum/rgb_matrix_animations/breathing_anim.h @@ -8,7 +8,7 @@ bool BREATHING(effect_params_t* params) { HSV hsv = rgb_matrix_config.hsv; uint16_t time = scale16by8(g_rgb_timer, rgb_matrix_config.speed / 8); hsv.v = scale8(abs8(sin8(time) - 128) * 2, hsv.v); - RGB rgb = hsv_to_rgb(hsv); + RGB rgb = rgb_matrix_hsv_to_rgb(hsv); for (uint8_t i = led_min; i < led_max; i++) { RGB_MATRIX_TEST_LED_FLAGS(); rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); diff --git a/quantum/rgb_matrix_animations/gradient_left_right_anim.h b/quantum/rgb_matrix_animations/gradient_left_right_anim.h index 2eab2eb7594f..53dfd04e2c85 100644 --- a/quantum/rgb_matrix_animations/gradient_left_right_anim.h +++ b/quantum/rgb_matrix_animations/gradient_left_right_anim.h @@ -12,7 +12,7 @@ bool GRADIENT_LEFT_RIGHT(effect_params_t* params) { // The x range will be 0..224, map this to 0..7 // Relies on hue being 8-bit and wrapping hsv.h = rgb_matrix_config.hsv.h + (scale * g_led_config.point[i].x >> 5); - RGB rgb = hsv_to_rgb(hsv); + RGB rgb = rgb_matrix_hsv_to_rgb(hsv); rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); } return led_max < DRIVER_LED_TOTAL; diff --git a/quantum/rgb_matrix_animations/gradient_up_down_anim.h b/quantum/rgb_matrix_animations/gradient_up_down_anim.h index 0f1f8e23cf22..7e0d2898cf7b 100644 --- a/quantum/rgb_matrix_animations/gradient_up_down_anim.h +++ b/quantum/rgb_matrix_animations/gradient_up_down_anim.h @@ -12,7 +12,7 @@ bool GRADIENT_UP_DOWN(effect_params_t* params) { // The y range will be 0..64, map this to 0..4 // Relies on hue being 8-bit and wrapping hsv.h = rgb_matrix_config.hsv.h + scale * (g_led_config.point[i].y >> 4); - RGB rgb = hsv_to_rgb(hsv); + RGB rgb = rgb_matrix_hsv_to_rgb(hsv); rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); } return led_max < DRIVER_LED_TOTAL; diff --git a/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h b/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h index ef2d1500b06f..9493b3850815 100644 --- a/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h +++ b/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h @@ -5,7 +5,7 @@ RGB_MATRIX_EFFECT(JELLYBEAN_RAINDROPS) static void jellybean_raindrops_set_color(int i, effect_params_t* params) { if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) return; HSV hsv = {rand() & 0xFF, rand() & 0xFF, rgb_matrix_config.hsv.v}; - RGB rgb = hsv_to_rgb(hsv); + RGB rgb = rgb_matrix_hsv_to_rgb(hsv); rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); } diff --git a/quantum/rgb_matrix_animations/raindrops_anim.h b/quantum/rgb_matrix_animations/raindrops_anim.h index 6e1b5acb0d49..38359cdca78b 100644 --- a/quantum/rgb_matrix_animations/raindrops_anim.h +++ b/quantum/rgb_matrix_animations/raindrops_anim.h @@ -15,7 +15,7 @@ static void raindrops_set_color(int i, effect_params_t* params) { } hsv.h = rgb_matrix_config.hsv.h + (deltaH * (rand() & 0x03)); - RGB rgb = hsv_to_rgb(hsv); + RGB rgb = rgb_matrix_hsv_to_rgb(hsv); rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); } diff --git a/quantum/rgb_matrix_animations/solid_color_anim.h b/quantum/rgb_matrix_animations/solid_color_anim.h index c8f5e70e7a64..79d63cf133b8 100644 --- a/quantum/rgb_matrix_animations/solid_color_anim.h +++ b/quantum/rgb_matrix_animations/solid_color_anim.h @@ -4,7 +4,7 @@ RGB_MATRIX_EFFECT(SOLID_COLOR) bool SOLID_COLOR(effect_params_t* params) { RGB_MATRIX_USE_LIMITS(led_min, led_max); - RGB rgb = hsv_to_rgb(rgb_matrix_config.hsv); + RGB rgb = rgb_matrix_hsv_to_rgb(rgb_matrix_config.hsv); for (uint8_t i = led_min; i < led_max; i++) { RGB_MATRIX_TEST_LED_FLAGS(); rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); diff --git a/quantum/rgb_matrix_animations/typing_heatmap_anim.h b/quantum/rgb_matrix_animations/typing_heatmap_anim.h index e82c1b49eeb9..b855fdc1907b 100644 --- a/quantum/rgb_matrix_animations/typing_heatmap_anim.h +++ b/quantum/rgb_matrix_animations/typing_heatmap_anim.h @@ -51,7 +51,7 @@ bool TYPING_HEATMAP(effect_params_t* params) { if (!HAS_ANY_FLAGS(g_led_config.flags[led[j]], params->flags)) continue; HSV hsv = {170 - qsub8(val, 85), rgb_matrix_config.hsv.s, scale8((qadd8(170, val) - 170) * 3, rgb_matrix_config.hsv.v)}; - RGB rgb = hsv_to_rgb(hsv); + RGB rgb = rgb_matrix_hsv_to_rgb(hsv); rgb_matrix_set_color(led[j], rgb.r, rgb.g, rgb.b); } diff --git a/quantum/rgb_matrix_runners/effect_runner_dx_dy.h b/quantum/rgb_matrix_runners/effect_runner_dx_dy.h index 9d0c9fab194a..4867609c81f9 100644 --- a/quantum/rgb_matrix_runners/effect_runner_dx_dy.h +++ b/quantum/rgb_matrix_runners/effect_runner_dx_dy.h @@ -10,7 +10,7 @@ bool effect_runner_dx_dy(effect_params_t* params, dx_dy_f effect_func) { RGB_MATRIX_TEST_LED_FLAGS(); int16_t dx = g_led_config.point[i].x - k_rgb_matrix_center.x; int16_t dy = g_led_config.point[i].y - k_rgb_matrix_center.y; - RGB rgb = hsv_to_rgb(effect_func(rgb_matrix_config.hsv, dx, dy, time)); + RGB rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, dx, dy, time)); rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); } return led_max < DRIVER_LED_TOTAL; diff --git a/quantum/rgb_matrix_runners/effect_runner_dx_dy_dist.h b/quantum/rgb_matrix_runners/effect_runner_dx_dy_dist.h index 2824c82527bf..9545b418d93b 100644 --- a/quantum/rgb_matrix_runners/effect_runner_dx_dy_dist.h +++ b/quantum/rgb_matrix_runners/effect_runner_dx_dy_dist.h @@ -11,7 +11,7 @@ bool effect_runner_dx_dy_dist(effect_params_t* params, dx_dy_dist_f effect_func) int16_t dx = g_led_config.point[i].x - k_rgb_matrix_center.x; int16_t dy = g_led_config.point[i].y - k_rgb_matrix_center.y; uint8_t dist = sqrt16(dx * dx + dy * dy); - RGB rgb = hsv_to_rgb(effect_func(rgb_matrix_config.hsv, dx, dy, dist, time)); + RGB rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, dx, dy, dist, time)); rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); } return led_max < DRIVER_LED_TOTAL; diff --git a/quantum/rgb_matrix_runners/effect_runner_i.h b/quantum/rgb_matrix_runners/effect_runner_i.h index 5e6bf5daaf32..95bfe8b390a7 100644 --- a/quantum/rgb_matrix_runners/effect_runner_i.h +++ b/quantum/rgb_matrix_runners/effect_runner_i.h @@ -8,7 +8,7 @@ bool effect_runner_i(effect_params_t* params, i_f effect_func) { uint8_t time = scale16by8(g_rgb_timer, rgb_matrix_config.speed / 4); for (uint8_t i = led_min; i < led_max; i++) { RGB_MATRIX_TEST_LED_FLAGS(); - RGB rgb = hsv_to_rgb(effect_func(rgb_matrix_config.hsv, i, time)); + RGB rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, i, time)); rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); } return led_max < DRIVER_LED_TOTAL; diff --git a/quantum/rgb_matrix_runners/effect_runner_reactive.h b/quantum/rgb_matrix_runners/effect_runner_reactive.h index 53e77e3fb262..8485b61f3d57 100644 --- a/quantum/rgb_matrix_runners/effect_runner_reactive.h +++ b/quantum/rgb_matrix_runners/effect_runner_reactive.h @@ -20,7 +20,7 @@ bool effect_runner_reactive(effect_params_t* params, reactive_f effect_func) { } uint16_t offset = scale16by8(tick, rgb_matrix_config.speed); - RGB rgb = hsv_to_rgb(effect_func(rgb_matrix_config.hsv, offset)); + RGB rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, offset)); rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); } return led_max < DRIVER_LED_TOTAL; diff --git a/quantum/rgb_matrix_runners/effect_runner_reactive_splash.h b/quantum/rgb_matrix_runners/effect_runner_reactive_splash.h index b5d284a40fb4..5c69d0fbb9ee 100644 --- a/quantum/rgb_matrix_runners/effect_runner_reactive_splash.h +++ b/quantum/rgb_matrix_runners/effect_runner_reactive_splash.h @@ -20,7 +20,7 @@ bool effect_runner_reactive_splash(uint8_t start, effect_params_t* params, react hsv = effect_func(hsv, dx, dy, dist, tick); } hsv.v = scale8(hsv.v, rgb_matrix_config.hsv.v); - RGB rgb = hsv_to_rgb(hsv); + RGB rgb = rgb_matrix_hsv_to_rgb(hsv); rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); } return led_max < DRIVER_LED_TOTAL; diff --git a/quantum/rgb_matrix_runners/effect_runner_sin_cos_i.h b/quantum/rgb_matrix_runners/effect_runner_sin_cos_i.h index 3fb7d4805176..02351de51e2e 100644 --- a/quantum/rgb_matrix_runners/effect_runner_sin_cos_i.h +++ b/quantum/rgb_matrix_runners/effect_runner_sin_cos_i.h @@ -10,7 +10,7 @@ bool effect_runner_sin_cos_i(effect_params_t* params, sin_cos_i_f effect_func) { int8_t sin_value = sin8(time) - 128; for (uint8_t i = led_min; i < led_max; i++) { RGB_MATRIX_TEST_LED_FLAGS(); - RGB rgb = hsv_to_rgb(effect_func(rgb_matrix_config.hsv, cos_value, sin_value, i, time)); + RGB rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, cos_value, sin_value, i, time)); rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); } return led_max < DRIVER_LED_TOTAL; diff --git a/quantum/rgblight.c b/quantum/rgblight.c index 76bb6eb8cb9a..7f9e330d371e 100644 --- a/quantum/rgblight.c +++ b/quantum/rgblight.c @@ -123,9 +123,11 @@ void rgblight_set_effect_range(uint8_t start_pos, uint8_t num_leds) { rgblight_ranges.effect_num_leds = num_leds; } +__attribute__((weak)) RGB rgblight_hsv_to_rgb(HSV hsv) { return hsv_to_rgb(hsv); } + void sethsv_raw(uint8_t hue, uint8_t sat, uint8_t val, LED_TYPE *led1) { HSV hsv = {hue, sat, val}; - RGB rgb = hsv_to_rgb(hsv); + RGB rgb = rgblight_hsv_to_rgb(hsv); setrgb(rgb.r, rgb.g, rgb.b, led1); } diff --git a/quantum/sequencer/sequencer.c b/quantum/sequencer/sequencer.c new file mode 100644 index 000000000000..0eaf3a17aa18 --- /dev/null +++ b/quantum/sequencer/sequencer.c @@ -0,0 +1,275 @@ +/* Copyright 2020 Rodolphe Belouin + * + * 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 . + */ + +#include "sequencer.h" + +#ifdef MIDI_ENABLE +# include "process_midi.h" +#endif + +#ifdef MIDI_MOCKED +# include "tests/midi_mock.h" +#endif + +sequencer_config_t sequencer_config = { + false, // enabled + {false}, // steps + {0}, // track notes + 60, // tempo + SQ_RES_4, // resolution +}; + +sequencer_state_t sequencer_internal_state = {0, 0, 0, 0, SEQUENCER_PHASE_ATTACK}; + +bool is_sequencer_on(void) { return sequencer_config.enabled; } + +void sequencer_on(void) { + dprintln("sequencer on"); + sequencer_config.enabled = true; + sequencer_internal_state.current_track = 0; + sequencer_internal_state.current_step = 0; + sequencer_internal_state.timer = timer_read(); + sequencer_internal_state.phase = SEQUENCER_PHASE_ATTACK; +} + +void sequencer_off(void) { + dprintln("sequencer off"); + sequencer_config.enabled = false; + sequencer_internal_state.current_step = 0; +} + +void sequencer_toggle(void) { + if (is_sequencer_on()) { + sequencer_off(); + } else { + sequencer_on(); + } +} + +void sequencer_set_track_notes(const uint16_t track_notes[SEQUENCER_TRACKS]) { + for (uint8_t i = 0; i < SEQUENCER_TRACKS; i++) { + sequencer_config.track_notes[i] = track_notes[i]; + } +} + +bool is_sequencer_track_active(uint8_t track) { return (sequencer_internal_state.active_tracks >> track) & true; } + +void sequencer_set_track_activation(uint8_t track, bool value) { + if (value) { + sequencer_internal_state.active_tracks |= (1 << track); + } else { + sequencer_internal_state.active_tracks &= ~(1 << track); + } + dprintf("sequencer: track %d is %s\n", track, value ? "active" : "inactive"); +} + +void sequencer_toggle_track_activation(uint8_t track) { sequencer_set_track_activation(track, !is_sequencer_track_active(track)); } + +void sequencer_toggle_single_active_track(uint8_t track) { + if (is_sequencer_track_active(track)) { + sequencer_internal_state.active_tracks = 0; + } else { + sequencer_internal_state.active_tracks = 1 << track; + } +} + +bool is_sequencer_step_on(uint8_t step) { return step < SEQUENCER_STEPS && (sequencer_config.steps[step] & sequencer_internal_state.active_tracks) > 0; } + +bool is_sequencer_step_on_for_track(uint8_t step, uint8_t track) { return step < SEQUENCER_STEPS && (sequencer_config.steps[step] >> track) & true; } + +void sequencer_set_step(uint8_t step, bool value) { + if (step < SEQUENCER_STEPS) { + if (value) { + sequencer_config.steps[step] |= sequencer_internal_state.active_tracks; + } else { + sequencer_config.steps[step] &= ~sequencer_internal_state.active_tracks; + } + dprintf("sequencer: step %d is %s\n", step, value ? "on" : "off"); + } else { + dprintf("sequencer: step %d is out of range\n", step); + } +} + +void sequencer_toggle_step(uint8_t step) { + if (is_sequencer_step_on(step)) { + sequencer_set_step_off(step); + } else { + sequencer_set_step_on(step); + } +} + +void sequencer_set_all_steps(bool value) { + for (uint8_t step = 0; step < SEQUENCER_STEPS; step++) { + if (value) { + sequencer_config.steps[step] |= sequencer_internal_state.active_tracks; + } else { + sequencer_config.steps[step] &= ~sequencer_internal_state.active_tracks; + } + } + dprintf("sequencer: all steps are %s\n", value ? "on" : "off"); +} + +uint8_t sequencer_get_tempo(void) { return sequencer_config.tempo; } + +void sequencer_set_tempo(uint8_t tempo) { + if (tempo > 0) { + sequencer_config.tempo = tempo; + dprintf("sequencer: tempo set to %d bpm\n", tempo); + } else { + dprintln("sequencer: cannot set tempo to 0"); + } +} + +void sequencer_increase_tempo(void) { + // Handling potential uint8_t overflow + if (sequencer_config.tempo < UINT8_MAX) { + sequencer_set_tempo(sequencer_config.tempo + 1); + } else { + dprintf("sequencer: cannot set tempo above %d\n", UINT8_MAX); + } +} + +void sequencer_decrease_tempo(void) { sequencer_set_tempo(sequencer_config.tempo - 1); } + +sequencer_resolution_t sequencer_get_resolution(void) { return sequencer_config.resolution; } + +void sequencer_set_resolution(sequencer_resolution_t resolution) { + if (resolution >= 0 && resolution < SEQUENCER_RESOLUTIONS) { + sequencer_config.resolution = resolution; + dprintf("sequencer: resolution set to %d\n", resolution); + } else { + dprintf("sequencer: resolution %d is out of range\n", resolution); + } +} + +void sequencer_increase_resolution(void) { sequencer_set_resolution(sequencer_config.resolution + 1); } + +void sequencer_decrease_resolution(void) { sequencer_set_resolution(sequencer_config.resolution - 1); } + +uint8_t sequencer_get_current_step(void) { return sequencer_internal_state.current_step; } + +void sequencer_phase_attack(void) { + dprintf("sequencer: step %d\n", sequencer_internal_state.current_step); + dprintf("sequencer: time %d\n", timer_read()); + + if (sequencer_internal_state.current_track == 0) { + sequencer_internal_state.timer = timer_read(); + } + + if (timer_elapsed(sequencer_internal_state.timer) < sequencer_internal_state.current_track * SEQUENCER_TRACK_THROTTLE) { + return; + } + +#if defined(MIDI_ENABLE) || defined(MIDI_MOCKED) + if (is_sequencer_step_on_for_track(sequencer_internal_state.current_step, sequencer_internal_state.current_track)) { + process_midi_basic_noteon(midi_compute_note(sequencer_config.track_notes[sequencer_internal_state.current_track])); + } +#endif + + if (sequencer_internal_state.current_track < SEQUENCER_TRACKS - 1) { + sequencer_internal_state.current_track++; + } else { + sequencer_internal_state.phase = SEQUENCER_PHASE_RELEASE; + } +} + +void sequencer_phase_release(void) { + if (timer_elapsed(sequencer_internal_state.timer) < SEQUENCER_PHASE_RELEASE_TIMEOUT + sequencer_internal_state.current_track * SEQUENCER_TRACK_THROTTLE) { + return; + } +#if defined(MIDI_ENABLE) || defined(MIDI_MOCKED) + if (is_sequencer_step_on_for_track(sequencer_internal_state.current_step, sequencer_internal_state.current_track)) { + process_midi_basic_noteoff(midi_compute_note(sequencer_config.track_notes[sequencer_internal_state.current_track])); + } +#endif + if (sequencer_internal_state.current_track > 0) { + sequencer_internal_state.current_track--; + } else { + sequencer_internal_state.phase = SEQUENCER_PHASE_PAUSE; + } +} + +void sequencer_phase_pause(void) { + if (timer_elapsed(sequencer_internal_state.timer) < sequencer_get_step_duration()) { + return; + } + + sequencer_internal_state.current_step = (sequencer_internal_state.current_step + 1) % SEQUENCER_STEPS; + sequencer_internal_state.phase = SEQUENCER_PHASE_ATTACK; +} + +void matrix_scan_sequencer(void) { + if (!sequencer_config.enabled) { + return; + } + + if (sequencer_internal_state.phase == SEQUENCER_PHASE_PAUSE) { + sequencer_phase_pause(); + } + + if (sequencer_internal_state.phase == SEQUENCER_PHASE_RELEASE) { + sequencer_phase_release(); + } + + if (sequencer_internal_state.phase == SEQUENCER_PHASE_ATTACK) { + sequencer_phase_attack(); + } +} + +uint16_t sequencer_get_beat_duration(void) { return get_beat_duration(sequencer_config.tempo); } + +uint16_t sequencer_get_step_duration(void) { return get_step_duration(sequencer_config.tempo, sequencer_config.resolution); } + +uint16_t get_beat_duration(uint8_t tempo) { + // Don’t crash in the unlikely case where the given tempo is 0 + if (tempo == 0) { + return get_beat_duration(60); + } + + /** + * Given + * t = tempo and d = duration, both strictly greater than 0 + * When + * t beats / minute = 1 beat / d ms + * Then + * t beats / 60000ms = 1 beat / d ms + * d ms = 60000ms / t + */ + return 60000 / tempo; +} + +uint16_t get_step_duration(uint8_t tempo, sequencer_resolution_t resolution) { + /** + * Resolution cheatsheet: + * 1/2 => 2 steps per 4 beats + * 1/2T => 3 steps per 4 beats + * 1/4 => 4 steps per 4 beats + * 1/4T => 6 steps per 4 beats + * 1/8 => 8 steps per 4 beats + * 1/8T => 12 steps per 4 beats + * 1/16 => 16 steps per 4 beats + * 1/16T => 24 steps per 4 beats + * 1/32 => 32 steps per 4 beats + * + * The number of steps for binary resolutions follows the powers of 2. + * The ternary variants are simply 1.5x faster. + */ + bool is_binary = resolution % 2 == 0; + uint8_t binary_steps = 2 << (resolution / 2); + uint16_t binary_step_duration = get_beat_duration(tempo) * 4 / binary_steps; + + return is_binary ? binary_step_duration : 2 * binary_step_duration / 3; +} diff --git a/quantum/sequencer/sequencer.h b/quantum/sequencer/sequencer.h new file mode 100644 index 000000000000..aeca7a1e9b10 --- /dev/null +++ b/quantum/sequencer/sequencer.h @@ -0,0 +1,122 @@ +/* Copyright 2020 Rodolphe Belouin + * + * 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 . + */ + +#pragma once + +#include +#include "debug.h" +#include "timer.h" + +// Maximum number of steps: 256 +#ifndef SEQUENCER_STEPS +# define SEQUENCER_STEPS 16 +#endif + +// Maximum number of tracks: 8 +#ifndef SEQUENCER_TRACKS +# define SEQUENCER_TRACKS 8 +#endif + +#ifndef SEQUENCER_TRACK_THROTTLE +# define SEQUENCER_TRACK_THROTTLE 3 +#endif + +#ifndef SEQUENCER_PHASE_RELEASE_TIMEOUT +# define SEQUENCER_PHASE_RELEASE_TIMEOUT 30 +#endif + +/** + * Make sure that the items of this enumeration follow the powers of 2, separated by a ternary variant. + * Check the implementation of `get_step_duration` for further explanation. + */ +typedef enum { SQ_RES_2, SQ_RES_2T, SQ_RES_4, SQ_RES_4T, SQ_RES_8, SQ_RES_8T, SQ_RES_16, SQ_RES_16T, SQ_RES_32, SEQUENCER_RESOLUTIONS } sequencer_resolution_t; + +typedef struct { + bool enabled; + uint8_t steps[SEQUENCER_STEPS]; + uint16_t track_notes[SEQUENCER_TRACKS]; + uint8_t tempo; // Is a maximum tempo of 255 reasonable? + sequencer_resolution_t resolution; +} sequencer_config_t; + +/** + * Because Digital Audio Workstations get overwhelmed when too many MIDI signals are sent concurrently, + * We use a "phase" state machine to delay some of the events. + */ +typedef enum sequencer_phase_t { + SEQUENCER_PHASE_ATTACK, // t=0ms, send the MIDI note on signal + SEQUENCER_PHASE_RELEASE, // t=SEQUENCER_PHASE_RELEASE_TIMEOUT ms, send the MIDI note off signal + SEQUENCER_PHASE_PAUSE // t=step duration ms, loop +} sequencer_phase_t; + +typedef struct { + uint8_t active_tracks; + uint8_t current_track; + uint8_t current_step; + uint16_t timer; + sequencer_phase_t phase; +} sequencer_state_t; + +extern sequencer_config_t sequencer_config; + +// We expose the internal state to make the feature more "unit-testable" +extern sequencer_state_t sequencer_internal_state; + +bool is_sequencer_on(void); +void sequencer_toggle(void); +void sequencer_on(void); +void sequencer_off(void); + +void sequencer_set_track_notes(const uint16_t track_notes[SEQUENCER_TRACKS]); + +bool is_sequencer_track_active(uint8_t track); +void sequencer_set_track_activation(uint8_t track, bool value); +void sequencer_toggle_track_activation(uint8_t track); +void sequencer_toggle_single_active_track(uint8_t track); + +#define sequencer_activate_track(track) sequencer_set_track_activation(track, true) +#define sequencer_deactivate_track(track) sequencer_set_track_activation(track, false) + +bool is_sequencer_step_on(uint8_t step); +bool is_sequencer_step_on_for_track(uint8_t step, uint8_t track); +void sequencer_set_step(uint8_t step, bool value); +void sequencer_toggle_step(uint8_t step); +void sequencer_set_all_steps(bool value); + +#define sequencer_set_step_on(step) sequencer_set_step(step, true) +#define sequencer_set_step_off(step) sequencer_set_step(step, false) +#define sequencer_set_all_steps_on() sequencer_set_all_steps(true) +#define sequencer_set_all_steps_off() sequencer_set_all_steps(false) + +uint8_t sequencer_get_tempo(void); +void sequencer_set_tempo(uint8_t tempo); +void sequencer_increase_tempo(void); +void sequencer_decrease_tempo(void); + +sequencer_resolution_t sequencer_get_resolution(void); +void sequencer_set_resolution(sequencer_resolution_t resolution); +void sequencer_increase_resolution(void); +void sequencer_decrease_resolution(void); + +uint8_t sequencer_get_current_step(void); + +uint16_t sequencer_get_beat_duration(void); +uint16_t sequencer_get_step_duration(void); + +uint16_t get_beat_duration(uint8_t tempo); +uint16_t get_step_duration(uint8_t tempo, sequencer_resolution_t resolution); + +void matrix_scan_sequencer(void); diff --git a/quantum/sequencer/tests/midi_mock.c b/quantum/sequencer/tests/midi_mock.c new file mode 100644 index 000000000000..236e16f9d70e --- /dev/null +++ b/quantum/sequencer/tests/midi_mock.c @@ -0,0 +1,26 @@ +/* Copyright 2020 Rodolphe Belouin + * + * 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 . + */ + +#include "midi_mock.h" + +uint16_t last_noteon = 0; +uint16_t last_noteoff = 0; + +uint16_t midi_compute_note(uint16_t keycode) { return keycode; } + +void process_midi_basic_noteon(uint16_t note) { last_noteon = note; } + +void process_midi_basic_noteoff(uint16_t note) { last_noteoff = note; } diff --git a/quantum/sequencer/tests/midi_mock.h b/quantum/sequencer/tests/midi_mock.h new file mode 100644 index 000000000000..4d8c2eb307b4 --- /dev/null +++ b/quantum/sequencer/tests/midi_mock.h @@ -0,0 +1,26 @@ +/* Copyright 2020 Rodolphe Belouin + * + * 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 . + */ + +#pragma once + +#include + +extern uint16_t last_noteon; +extern uint16_t last_noteoff; + +uint16_t midi_compute_note(uint16_t keycode); +void process_midi_basic_noteon(uint16_t note); +void process_midi_basic_noteoff(uint16_t note); diff --git a/quantum/sequencer/tests/rules.mk b/quantum/sequencer/tests/rules.mk new file mode 100644 index 000000000000..76c221cf92aa --- /dev/null +++ b/quantum/sequencer/tests/rules.mk @@ -0,0 +1,11 @@ +# The letter case of these variables might seem odd. However: +# - it is consistent with the serial_link example that is used as a reference in the Unit Testing article (https://docs.qmk.fm/#/unit_testing?id=adding-tests-for-new-or-existing-features) +# - Neither `make test:sequencer` or `make test:SEQUENCER` work when using SCREAMING_SNAKE_CASE + +sequencer_DEFS := -DNO_DEBUG -DMIDI_MOCKED + +sequencer_SRC := \ + $(QUANTUM_PATH)/sequencer/tests/midi_mock.c \ + $(QUANTUM_PATH)/sequencer/tests/sequencer_tests.cpp \ + $(QUANTUM_PATH)/sequencer/sequencer.c \ + $(TMK_PATH)/common/test/timer.c diff --git a/quantum/sequencer/tests/sequencer_tests.cpp b/quantum/sequencer/tests/sequencer_tests.cpp new file mode 100644 index 000000000000..e81984e5b51b --- /dev/null +++ b/quantum/sequencer/tests/sequencer_tests.cpp @@ -0,0 +1,590 @@ +/* Copyright 2020 Rodolphe Belouin + * + * 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 . + */ + +#include "gtest/gtest.h" + +extern "C" { +#include "sequencer.h" +#include "midi_mock.h" +#include "quantum/quantum_keycodes.h" +} + +extern "C" { +void set_time(uint32_t t); +void advance_time(uint32_t ms); +} + +class SequencerTest : public ::testing::Test { + protected: + void SetUp() override { + config_copy.enabled = sequencer_config.enabled; + + for (int i = 0; i < SEQUENCER_STEPS; i++) { + config_copy.steps[i] = sequencer_config.steps[i]; + } + + for (int i = 0; i < SEQUENCER_TRACKS; i++) { + config_copy.track_notes[i] = sequencer_config.track_notes[i]; + } + + config_copy.tempo = sequencer_config.tempo; + config_copy.resolution = sequencer_config.resolution; + + state_copy.active_tracks = sequencer_internal_state.active_tracks; + state_copy.current_track = sequencer_internal_state.current_track; + state_copy.current_step = sequencer_internal_state.current_step; + state_copy.timer = sequencer_internal_state.timer; + + last_noteon = 0; + last_noteoff = 0; + + set_time(0); + } + + void TearDown() override { + sequencer_config.enabled = config_copy.enabled; + + for (int i = 0; i < SEQUENCER_STEPS; i++) { + sequencer_config.steps[i] = config_copy.steps[i]; + } + + for (int i = 0; i < SEQUENCER_TRACKS; i++) { + sequencer_config.track_notes[i] = config_copy.track_notes[i]; + } + + sequencer_config.tempo = config_copy.tempo; + sequencer_config.resolution = config_copy.resolution; + + sequencer_internal_state.active_tracks = state_copy.active_tracks; + sequencer_internal_state.current_track = state_copy.current_track; + sequencer_internal_state.current_step = state_copy.current_step; + sequencer_internal_state.timer = state_copy.timer; + } + + sequencer_config_t config_copy; + sequencer_state_t state_copy; +}; + +TEST_F(SequencerTest, TestOffByDefault) { EXPECT_EQ(is_sequencer_on(), false); } + +TEST_F(SequencerTest, TestOn) { + sequencer_config.enabled = false; + + sequencer_on(); + EXPECT_EQ(is_sequencer_on(), true); + + // sequencer_on is idempotent + sequencer_on(); + EXPECT_EQ(is_sequencer_on(), true); +} + +TEST_F(SequencerTest, TestOff) { + sequencer_config.enabled = true; + + sequencer_off(); + EXPECT_EQ(is_sequencer_on(), false); + + // sequencer_off is idempotent + sequencer_off(); + EXPECT_EQ(is_sequencer_on(), false); +} + +TEST_F(SequencerTest, TestToggle) { + sequencer_config.enabled = false; + + sequencer_toggle(); + EXPECT_EQ(is_sequencer_on(), true); + + sequencer_toggle(); + EXPECT_EQ(is_sequencer_on(), false); +} + +TEST_F(SequencerTest, TestNoActiveTrackByDefault) { + for (int i = 0; i < SEQUENCER_TRACKS; i++) { + EXPECT_EQ(is_sequencer_track_active(i), false); + } +} + +TEST_F(SequencerTest, TestGetActiveTracks) { + sequencer_internal_state.active_tracks = (1 << 7) + (1 << 6) + (1 << 3) + (1 << 1) + (1 << 0); + + EXPECT_EQ(is_sequencer_track_active(0), true); + EXPECT_EQ(is_sequencer_track_active(1), true); + EXPECT_EQ(is_sequencer_track_active(2), false); + EXPECT_EQ(is_sequencer_track_active(3), true); + EXPECT_EQ(is_sequencer_track_active(4), false); + EXPECT_EQ(is_sequencer_track_active(5), false); + EXPECT_EQ(is_sequencer_track_active(6), true); + EXPECT_EQ(is_sequencer_track_active(7), true); +} + +TEST_F(SequencerTest, TestGetActiveTracksOutOfBound) { + sequencer_set_track_activation(-1, true); + sequencer_set_track_activation(8, true); + + EXPECT_EQ(is_sequencer_track_active(-1), false); + EXPECT_EQ(is_sequencer_track_active(8), false); +} + +TEST_F(SequencerTest, TestToggleTrackActivation) { + sequencer_internal_state.active_tracks = (1 << 7) + (1 << 6) + (1 << 3) + (1 << 1) + (1 << 0); + + sequencer_toggle_track_activation(6); + + EXPECT_EQ(is_sequencer_track_active(0), true); + EXPECT_EQ(is_sequencer_track_active(1), true); + EXPECT_EQ(is_sequencer_track_active(2), false); + EXPECT_EQ(is_sequencer_track_active(3), true); + EXPECT_EQ(is_sequencer_track_active(4), false); + EXPECT_EQ(is_sequencer_track_active(5), false); + EXPECT_EQ(is_sequencer_track_active(6), false); + EXPECT_EQ(is_sequencer_track_active(7), true); +} + +TEST_F(SequencerTest, TestToggleSingleTrackActivation) { + sequencer_internal_state.active_tracks = (1 << 7) + (1 << 6) + (1 << 3) + (1 << 1) + (1 << 0); + + sequencer_toggle_single_active_track(2); + + EXPECT_EQ(is_sequencer_track_active(0), false); + EXPECT_EQ(is_sequencer_track_active(1), false); + EXPECT_EQ(is_sequencer_track_active(2), true); + EXPECT_EQ(is_sequencer_track_active(3), false); + EXPECT_EQ(is_sequencer_track_active(4), false); + EXPECT_EQ(is_sequencer_track_active(5), false); + EXPECT_EQ(is_sequencer_track_active(6), false); + EXPECT_EQ(is_sequencer_track_active(7), false); +} + +TEST_F(SequencerTest, TestStepOffByDefault) { + for (int i = 0; i < SEQUENCER_STEPS; i++) { + EXPECT_EQ(is_sequencer_step_on(i), false); + } +} + +TEST_F(SequencerTest, TestIsStepOffWithNoActiveTracks) { + sequencer_config.steps[3] = 0xFF; + EXPECT_EQ(is_sequencer_step_on(3), false); +} + +TEST_F(SequencerTest, TestIsStepOffWithGivenActiveTracks) { + sequencer_set_track_activation(2, true); + sequencer_set_track_activation(3, true); + + sequencer_config.steps[3] = (1 << 0) + (1 << 1); + + // No active tracks have the step enabled, so it is off + EXPECT_EQ(is_sequencer_step_on(3), false); +} + +TEST_F(SequencerTest, TestIsStepOnWithGivenActiveTracks) { + sequencer_set_track_activation(2, true); + sequencer_set_track_activation(3, true); + + sequencer_config.steps[3] = (1 << 2); + + // Track 2 has the step enabled, so it is on + EXPECT_EQ(is_sequencer_step_on(3), true); +} + +TEST_F(SequencerTest, TestIsStepOffForGivenTrack) { + sequencer_config.steps[3] = 0x00; + EXPECT_EQ(is_sequencer_step_on_for_track(3, 5), false); +} + +TEST_F(SequencerTest, TestIsStepOnForGivenTrack) { + sequencer_config.steps[3] = (1 << 5); + EXPECT_EQ(is_sequencer_step_on_for_track(3, 5), true); +} + +TEST_F(SequencerTest, TestSetStepOn) { + sequencer_internal_state.active_tracks = (1 << 6) + (1 << 3) + (1 << 2); + sequencer_config.steps[2] = (1 << 5) + (1 << 2); + + sequencer_set_step(2, true); + + EXPECT_EQ(sequencer_config.steps[2], (1 << 6) + (1 << 5) + (1 << 3) + (1 << 2)); +} + +TEST_F(SequencerTest, TestSetStepOff) { + sequencer_internal_state.active_tracks = (1 << 6) + (1 << 3) + (1 << 2); + sequencer_config.steps[2] = (1 << 5) + (1 << 2); + + sequencer_set_step(2, false); + + EXPECT_EQ(sequencer_config.steps[2], (1 << 5)); +} + +TEST_F(SequencerTest, TestToggleStepOff) { + sequencer_internal_state.active_tracks = (1 << 6) + (1 << 3) + (1 << 2); + sequencer_config.steps[2] = (1 << 5) + (1 << 2); + + sequencer_toggle_step(2); + + EXPECT_EQ(sequencer_config.steps[2], (1 << 5)); +} + +TEST_F(SequencerTest, TestToggleStepOn) { + sequencer_internal_state.active_tracks = (1 << 6) + (1 << 3) + (1 << 2); + sequencer_config.steps[2] = 0; + + sequencer_toggle_step(2); + + EXPECT_EQ(sequencer_config.steps[2], (1 << 6) + (1 << 3) + (1 << 2)); +} + +TEST_F(SequencerTest, TestSetAllStepsOn) { + sequencer_internal_state.active_tracks = (1 << 6) + (1 << 3) + (1 << 2); + sequencer_config.steps[2] = (1 << 7) + (1 << 6); + sequencer_config.steps[4] = (1 << 3) + (1 << 1); + + sequencer_set_all_steps(true); + + EXPECT_EQ(sequencer_config.steps[2], (1 << 7) + (1 << 6) + (1 << 3) + (1 << 2)); + EXPECT_EQ(sequencer_config.steps[4], (1 << 6) + (1 << 3) + (1 << 2) + (1 << 1)); +} + +TEST_F(SequencerTest, TestSetAllStepsOff) { + sequencer_internal_state.active_tracks = (1 << 6) + (1 << 3) + (1 << 2); + sequencer_config.steps[2] = (1 << 7) + (1 << 6); + sequencer_config.steps[4] = (1 << 3) + (1 << 1); + + sequencer_set_all_steps(false); + + EXPECT_EQ(sequencer_config.steps[2], (1 << 7)); + EXPECT_EQ(sequencer_config.steps[4], (1 << 1)); +} + +TEST_F(SequencerTest, TestSetTempoZero) { + sequencer_config.tempo = 123; + + sequencer_set_tempo(0); + + EXPECT_EQ(sequencer_config.tempo, 123); +} + +TEST_F(SequencerTest, TestIncreaseTempoMax) { + sequencer_config.tempo = UINT8_MAX; + + sequencer_increase_tempo(); + + EXPECT_EQ(sequencer_config.tempo, UINT8_MAX); +} + +TEST_F(SequencerTest, TestSetResolutionLowerBound) { + sequencer_config.resolution = SQ_RES_4; + + sequencer_set_resolution((sequencer_resolution_t)-1); + + EXPECT_EQ(sequencer_config.resolution, SQ_RES_4); +} + +TEST_F(SequencerTest, TestSetResolutionUpperBound) { + sequencer_config.resolution = SQ_RES_4; + + sequencer_set_resolution(SEQUENCER_RESOLUTIONS); + + EXPECT_EQ(sequencer_config.resolution, SQ_RES_4); +} + +TEST_F(SequencerTest, TestGetBeatDuration) { + EXPECT_EQ(get_beat_duration(60), 1000); + EXPECT_EQ(get_beat_duration(120), 500); + EXPECT_EQ(get_beat_duration(240), 250); + EXPECT_EQ(get_beat_duration(0), 1000); +} + +TEST_F(SequencerTest, TestGetStepDuration60) { + /** + * Resolution cheatsheet: + * 1/2 => 2 steps per 4 beats + * 1/2T => 3 steps per 4 beats + * 1/4 => 4 steps per 4 beats + * 1/4T => 6 steps per 4 beats + * 1/8 => 8 steps per 4 beats + * 1/8T => 12 steps per 4 beats + * 1/16 => 16 steps per 4 beats + * 1/16T => 24 steps per 4 beats + * 1/32 => 32 steps per 4 beats + * + * The number of steps for binary resolutions follows the powers of 2. + * The ternary variants are simply 1.5x faster. + */ + EXPECT_EQ(get_step_duration(60, SQ_RES_2), 2000); + EXPECT_EQ(get_step_duration(60, SQ_RES_4), 1000); + EXPECT_EQ(get_step_duration(60, SQ_RES_8), 500); + EXPECT_EQ(get_step_duration(60, SQ_RES_16), 250); + EXPECT_EQ(get_step_duration(60, SQ_RES_32), 125); + + EXPECT_EQ(get_step_duration(60, SQ_RES_2T), 1333); + EXPECT_EQ(get_step_duration(60, SQ_RES_4T), 666); + EXPECT_EQ(get_step_duration(60, SQ_RES_8T), 333); + EXPECT_EQ(get_step_duration(60, SQ_RES_16T), 166); +} + +TEST_F(SequencerTest, TestGetStepDuration120) { + /** + * Resolution cheatsheet: + * 1/2 => 2 steps per 4 beats + * 1/2T => 3 steps per 4 beats + * 1/4 => 4 steps per 4 beats + * 1/4T => 6 steps per 4 beats + * 1/8 => 8 steps per 4 beats + * 1/8T => 12 steps per 4 beats + * 1/16 => 16 steps per 4 beats + * 1/16T => 24 steps per 4 beats + * 1/32 => 32 steps per 4 beats + * + * The number of steps for binary resolutions follows the powers of 2. + * The ternary variants are simply 1.5x faster. + */ + EXPECT_EQ(get_step_duration(30, SQ_RES_2), 4000); + EXPECT_EQ(get_step_duration(30, SQ_RES_4), 2000); + EXPECT_EQ(get_step_duration(30, SQ_RES_8), 1000); + EXPECT_EQ(get_step_duration(30, SQ_RES_16), 500); + EXPECT_EQ(get_step_duration(30, SQ_RES_32), 250); + + EXPECT_EQ(get_step_duration(30, SQ_RES_2T), 2666); + EXPECT_EQ(get_step_duration(30, SQ_RES_4T), 1333); + EXPECT_EQ(get_step_duration(30, SQ_RES_8T), 666); + EXPECT_EQ(get_step_duration(30, SQ_RES_16T), 333); +} + +void setUpMatrixScanSequencerTest(void) { + sequencer_config.enabled = true; + sequencer_config.tempo = 120; + sequencer_config.resolution = SQ_RES_16; + + // Configure the notes for each track + sequencer_config.track_notes[0] = MI_C; + sequencer_config.track_notes[1] = MI_D; + sequencer_config.track_notes[2] = MI_E; + sequencer_config.track_notes[3] = MI_F; + sequencer_config.track_notes[4] = MI_G; + sequencer_config.track_notes[5] = MI_A; + sequencer_config.track_notes[6] = MI_B; + sequencer_config.track_notes[7] = MI_C; + + // Turn on some steps + sequencer_config.steps[0] = (1 << 0); + sequencer_config.steps[2] = (1 << 1) + (1 << 0); +} + +TEST_F(SequencerTest, TestMatrixScanSequencerShouldAttackFirstTrackOfFirstStep) { + setUpMatrixScanSequencerTest(); + + matrix_scan_sequencer(); + EXPECT_EQ(last_noteon, MI_C); + EXPECT_EQ(last_noteoff, 0); +} + +TEST_F(SequencerTest, TestMatrixScanSequencerShouldAttackSecondTrackAfterFirstTrackOfFirstStep) { + setUpMatrixScanSequencerTest(); + + matrix_scan_sequencer(); + EXPECT_EQ(sequencer_internal_state.current_step, 0); + EXPECT_EQ(sequencer_internal_state.current_track, 1); + EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_ATTACK); +} + +TEST_F(SequencerTest, TestMatrixScanSequencerShouldNotAttackInactiveTrackFirstStep) { + setUpMatrixScanSequencerTest(); + + sequencer_internal_state.current_step = 0; + sequencer_internal_state.current_track = 1; + + // Wait some time after the first track has been attacked + advance_time(SEQUENCER_TRACK_THROTTLE); + + matrix_scan_sequencer(); + EXPECT_EQ(last_noteon, 0); + EXPECT_EQ(last_noteoff, 0); +} + +TEST_F(SequencerTest, TestMatrixScanSequencerShouldAttackThirdTrackAfterSecondTrackOfFirstStep) { + setUpMatrixScanSequencerTest(); + + sequencer_internal_state.current_step = 0; + sequencer_internal_state.current_track = 1; + + // Wait some time after the second track has been attacked + advance_time(2 * SEQUENCER_TRACK_THROTTLE); + + matrix_scan_sequencer(); + EXPECT_EQ(sequencer_internal_state.current_step, 0); + EXPECT_EQ(sequencer_internal_state.current_track, 2); + EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_ATTACK); +} + +TEST_F(SequencerTest, TestMatrixScanSequencerShouldEnterReleasePhaseAfterLastTrackHasBeenProcessedFirstStep) { + setUpMatrixScanSequencerTest(); + + sequencer_internal_state.current_step = 0; + sequencer_internal_state.current_track = SEQUENCER_TRACKS - 1; + + // Wait until all notes have been attacked + advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE); + + matrix_scan_sequencer(); + EXPECT_EQ(last_noteon, 0); + EXPECT_EQ(last_noteoff, 0); + EXPECT_EQ(sequencer_internal_state.current_step, 0); + EXPECT_EQ(sequencer_internal_state.current_track, SEQUENCER_TRACKS - 1); + EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_RELEASE); +} + +TEST_F(SequencerTest, TestMatrixScanSequencerShouldReleaseBackwards) { + setUpMatrixScanSequencerTest(); + + sequencer_internal_state.current_step = 0; + sequencer_internal_state.current_track = SEQUENCER_TRACKS - 1; + sequencer_internal_state.phase = SEQUENCER_PHASE_RELEASE; + + // Wait until all notes have been attacked + advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE); + // + the release timeout + advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT); + + matrix_scan_sequencer(); + EXPECT_EQ(sequencer_internal_state.current_step, 0); + EXPECT_EQ(sequencer_internal_state.current_track, SEQUENCER_TRACKS - 2); + EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_RELEASE); +} + +TEST_F(SequencerTest, TestMatrixScanSequencerShouldNotReleaseInactiveTrackFirstStep) { + setUpMatrixScanSequencerTest(); + + sequencer_internal_state.current_step = 0; + sequencer_internal_state.current_track = SEQUENCER_TRACKS - 1; + sequencer_internal_state.phase = SEQUENCER_PHASE_RELEASE; + + // Wait until all notes have been attacked + advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE); + // + the release timeout + advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT); + + matrix_scan_sequencer(); + EXPECT_EQ(last_noteon, 0); + EXPECT_EQ(last_noteoff, 0); +} + +TEST_F(SequencerTest, TestMatrixScanSequencerShouldReleaseFirstTrackFirstStep) { + setUpMatrixScanSequencerTest(); + + sequencer_internal_state.current_step = 0; + sequencer_internal_state.current_track = 0; + sequencer_internal_state.phase = SEQUENCER_PHASE_RELEASE; + + // Wait until all notes have been attacked + advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE); + // + the release timeout + advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT); + // + all the other notes have been released + advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE); + + matrix_scan_sequencer(); + EXPECT_EQ(last_noteon, 0); + EXPECT_EQ(last_noteoff, MI_C); +} + +TEST_F(SequencerTest, TestMatrixScanSequencerShouldEnterPausePhaseAfterRelease) { + setUpMatrixScanSequencerTest(); + + sequencer_internal_state.current_step = 0; + sequencer_internal_state.current_track = 0; + sequencer_internal_state.phase = SEQUENCER_PHASE_RELEASE; + + // Wait until all notes have been attacked + advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE); + // + the release timeout + advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT); + // + all the other notes have been released + advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE); + + matrix_scan_sequencer(); + EXPECT_EQ(sequencer_internal_state.current_step, 0); + EXPECT_EQ(sequencer_internal_state.current_track, 0); + EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_PAUSE); +} + +TEST_F(SequencerTest, TestMatrixScanSequencerShouldProcessFirstTrackOfSecondStepAfterPause) { + setUpMatrixScanSequencerTest(); + + sequencer_internal_state.current_step = 0; + sequencer_internal_state.current_track = 0; + sequencer_internal_state.phase = SEQUENCER_PHASE_PAUSE; + + // Wait until all notes have been attacked + advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE); + // + the release timeout + advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT); + // + all the other notes have been released + advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE); + // + the step duration (one 16th at tempo=120 lasts 125ms) + advance_time(125); + + matrix_scan_sequencer(); + EXPECT_EQ(sequencer_internal_state.current_step, 1); + EXPECT_EQ(sequencer_internal_state.current_track, 1); + EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_ATTACK); +} + +TEST_F(SequencerTest, TestMatrixScanSequencerShouldProcessSecondTrackTooEarly) { + setUpMatrixScanSequencerTest(); + + sequencer_internal_state.current_step = 2; + sequencer_internal_state.current_track = 1; + + matrix_scan_sequencer(); + EXPECT_EQ(last_noteon, 0); + EXPECT_EQ(last_noteoff, 0); +} + +TEST_F(SequencerTest, TestMatrixScanSequencerShouldProcessSecondTrackOnTime) { + setUpMatrixScanSequencerTest(); + + sequencer_internal_state.current_step = 2; + sequencer_internal_state.current_track = 1; + + // Wait until first track has been attacked + advance_time(SEQUENCER_TRACK_THROTTLE); + + matrix_scan_sequencer(); + EXPECT_EQ(last_noteon, MI_D); + EXPECT_EQ(last_noteoff, 0); +} + +TEST_F(SequencerTest, TestMatrixScanSequencerShouldLoopOnceSequenceIsOver) { + setUpMatrixScanSequencerTest(); + + sequencer_internal_state.current_step = SEQUENCER_STEPS - 1; + sequencer_internal_state.current_track = 0; + sequencer_internal_state.phase = SEQUENCER_PHASE_PAUSE; + + // Wait until all notes have been attacked + advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE); + // + the release timeout + advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT); + // + all the other notes have been released + advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE); + // + the step duration (one 16th at tempo=120 lasts 125ms) + advance_time(125); + + matrix_scan_sequencer(); + EXPECT_EQ(sequencer_internal_state.current_step, 0); + EXPECT_EQ(sequencer_internal_state.current_track, 1); + EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_ATTACK); +} diff --git a/quantum/sequencer/tests/testlist.mk b/quantum/sequencer/tests/testlist.mk new file mode 100644 index 000000000000..bb3899110922 --- /dev/null +++ b/quantum/sequencer/tests/testlist.mk @@ -0,0 +1 @@ +TEST_LIST += sequencer diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c index 5bad9db08f8a..cd5a024c3df4 100644 --- a/quantum/split_common/matrix.c +++ b/quantum/split_common/matrix.c @@ -45,6 +45,19 @@ uint8_t thisHand, thatHand; // user-defined overridable functions __attribute__((weak)) void matrix_slave_scan_user(void) {} +static inline void setPinOutput_writeLow(pin_t pin) { + ATOMIC_BLOCK_FORCEON { + setPinOutput(pin); + writePinLow(pin); + } +} + +static inline void setPinInputHigh_atomic(pin_t pin) { + ATOMIC_BLOCK_FORCEON { + setPinInputHigh(pin); + } +} + // matrix code #ifdef DIRECT_PINS @@ -83,22 +96,23 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) # if (DIODE_DIRECTION == COL2ROW) static void select_row(uint8_t row) { - setPinOutput(row_pins[row]); - writePinLow(row_pins[row]); + setPinOutput_writeLow(row_pins[row]); } -static void unselect_row(uint8_t row) { setPinInputHigh(row_pins[row]); } +static void unselect_row(uint8_t row) { + setPinInputHigh_atomic(row_pins[row]); +} static void unselect_rows(void) { for (uint8_t x = 0; x < ROWS_PER_HAND; x++) { - setPinInputHigh(row_pins[x]); + setPinInputHigh_atomic(row_pins[x]); } } static void init_pins(void) { unselect_rows(); for (uint8_t x = 0; x < MATRIX_COLS; x++) { - setPinInputHigh(col_pins[x]); + setPinInputHigh_atomic(col_pins[x]); } } @@ -133,22 +147,23 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) # elif (DIODE_DIRECTION == ROW2COL) static void select_col(uint8_t col) { - setPinOutput(col_pins[col]); - writePinLow(col_pins[col]); + setPinOutput_writeLow(col_pins[col]); } -static void unselect_col(uint8_t col) { setPinInputHigh(col_pins[col]); } +static void unselect_col(uint8_t col) { + setPinInputHigh_atomic(col_pins[col]); +} static void unselect_cols(void) { for (uint8_t x = 0; x < MATRIX_COLS; x++) { - setPinInputHigh(col_pins[x]); + setPinInputHigh_atomic(col_pins[x]); } } static void init_pins(void) { unselect_cols(); for (uint8_t x = 0; x < ROWS_PER_HAND; x++) { - setPinInputHigh(row_pins[x]); + setPinInputHigh_atomic(row_pins[x]); } } diff --git a/requirements.txt b/requirements.txt index d20803577eab..6e907cf8e83c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ # Python requirements -# milc FIXME(skullydazed): Included in the repo for now. appdirs argcomplete colorama hjson +milc pygments diff --git a/show_options.mk b/show_options.mk index 2a5781773502..df3d03957120 100644 --- a/show_options.mk +++ b/show_options.mk @@ -20,6 +20,7 @@ HARDWARE_OPTION_NAMES = \ RGBLIGHT_ENABLE \ RGBLIGHT_CUSTOM_DRIVER \ RGB_MATRIX_ENABLE \ + RGB_MATRIX_DRIVER \ SERIAL_LINK_ENABLE \ CIE1931_CURVE \ MIDI_ENABLE \ diff --git a/testlist.mk b/testlist.mk index d949fb3eae01..ca1408806c9a 100644 --- a/testlist.mk +++ b/testlist.mk @@ -1,6 +1,7 @@ TEST_LIST = $(notdir $(patsubst %/rules.mk,%,$(wildcard $(ROOT_DIR)/tests/*/rules.mk))) FULL_TESTS := $(TEST_LIST) +include $(ROOT_DIR)/quantum/sequencer/tests/testlist.mk include $(ROOT_DIR)/quantum/serial_link/tests/testlist.mk define VALIDATE_TEST_LIST diff --git a/tmk_core/chibios.mk b/tmk_core/chibios.mk index 2bd53f0dc8c9..e53edccee21f 100644 --- a/tmk_core/chibios.mk +++ b/tmk_core/chibios.mk @@ -123,6 +123,8 @@ else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/chconf.h)","") CHCONFDIR = $(KEYBOARD_PATH_1) else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/$(BOARD)/configs/chconf.h)","") CHCONFDIR = $(TOP_DIR)/platforms/chibios/$(BOARD)/configs +else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/common/configs/chconf.h)","") + CHCONFDIR = $(TOP_DIR)/platforms/chibios/common/configs endif ifneq ("$(wildcard $(KEYBOARD_PATH_5)/halconf.h)","") @@ -137,6 +139,8 @@ else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/halconf.h)","") HALCONFDIR = $(KEYBOARD_PATH_1) else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/$(BOARD)/configs/halconf.h)","") HALCONFDIR = $(TOP_DIR)/platforms/chibios/$(BOARD)/configs +else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/common/configs/halconf.h)","") + HALCONFDIR = $(TOP_DIR)/platforms/chibios/common/configs endif # HAL-OSAL files (optional). @@ -183,8 +187,8 @@ else ifneq ("$(wildcard $(KEYBOARD_PATH_2)/ld/$(MCU_LDSCRIPT).ld)","") LDSCRIPT = $(KEYBOARD_PATH_2)/ld/$(MCU_LDSCRIPT).ld else ifneq ("$(wildcard $(KEYBOARD_PATH_1)/ld/$(MCU_LDSCRIPT).ld)","") LDSCRIPT = $(KEYBOARD_PATH_1)/ld/$(MCU_LDSCRIPT).ld -else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/ld/$(MCU_LDSCRIPT).ld)","") - LDSCRIPT = $(TOP_DIR)/platforms/chibios/ld/$(MCU_LDSCRIPT).ld +else ifneq ("$(wildcard $(TOP_DIR)/platforms/chibios/common/ld/$(MCU_LDSCRIPT).ld)","") + LDSCRIPT = $(TOP_DIR)/platforms/chibios/common/ld/$(MCU_LDSCRIPT).ld else ifneq ("$(wildcard $(STARTUPLD_CONTRIB)/$(MCU_LDSCRIPT).ld)","") LDSCRIPT = $(STARTUPLD_CONTRIB)/$(MCU_LDSCRIPT).ld USE_CHIBIOS_CONTRIB = yes @@ -209,6 +213,8 @@ CHIBISRC := $(patsubst $(TOP_DIR)/%,%,$(CHIBISRC)) EXTRAINCDIRS += $(CHIBIOS)/os/license $(CHIBIOS)/os/oslib/include \ $(TOP_DIR)/platforms/chibios/$(BOARD)/configs \ + $(TOP_DIR)/platforms/chibios/common/configs \ + $(HALCONFDIR) $(CHCONFDIR) \ $(STARTUPINC) $(KERNINC) $(PORTINC) $(OSALINC) \ $(HALINC) $(PLATFORMINC) $(BOARDINC) $(TESTINC) \ $(STREAMSINC) $(CHIBIOS)/os/various $(COMMON_VPATH) diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c index ee9aa0df77db..77da0139f22f 100644 --- a/tmk_core/common/action.c +++ b/tmk_core/common/action.c @@ -37,9 +37,13 @@ along with this program. If not, see . # include "nodebug.h" #endif +#ifdef POINTING_DEVICE_ENABLE +# include "pointing_device.h" +#endif + int tp_buttons; -#ifdef RETRO_TAPPING +#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) int retro_tapping_counter = 0; #endif @@ -51,6 +55,10 @@ int retro_tapping_counter = 0; __attribute__((weak)) bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record) { return false; } #endif +#ifdef RETRO_TAPPING_PER_KEY +__attribute__((weak)) bool get_retro_tapping(uint16_t keycode, keyrecord_t *record) { return false; } +#endif + #ifndef TAP_CODE_DELAY # define TAP_CODE_DELAY 0 #endif @@ -67,7 +75,7 @@ void action_exec(keyevent_t event) { dprint("EVENT: "); debug_event(event); dprintln(); -#ifdef RETRO_TAPPING +#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) retro_tapping_counter++; #endif } @@ -220,6 +228,19 @@ void process_record_handler(keyrecord_t *record) { process_action(record, action); } +#if defined(PS2_MOUSE_ENABLE) || defined(POINTING_DEVICE_ENABLE) +void register_button(bool pressed, enum mouse_buttons button) { +# ifdef PS2_MOUSE_ENABLE + tp_buttons = pressed ? tp_buttons | button : tp_buttons & ~button; +# endif +# ifdef POINTING_DEVICE_ENABLE + report_mouse_t currentReport = pointing_device_get_report(); + currentReport.buttons = pressed ? currentReport.buttons | button : currentReport.buttons & ~button; + pointing_device_set_report(currentReport); +# endif +} +#endif + /** \brief Take an action and processes it. * * FIXME: Needs documentation. @@ -404,15 +425,23 @@ void process_action(keyrecord_t *record, action_t action) { if (event.pressed) { mousekey_on(action.key.code); switch (action.key.code) { -# ifdef PS2_MOUSE_ENABLE +# if defined(PS2_MOUSE_ENABLE) || defined(POINTING_DEVICE_ENABLE) case KC_MS_BTN1: - tp_buttons |= (1 << 0); + register_button(true, MOUSE_BTN1); break; case KC_MS_BTN2: - tp_buttons |= (1 << 1); + register_button(true, MOUSE_BTN2); break; case KC_MS_BTN3: - tp_buttons |= (1 << 2); + register_button(true, MOUSE_BTN3); + break; +# endif +# ifdef POINTING_DEVICE_ENABLE + case KC_MS_BTN4: + register_button(true, MOUSE_BTN4); + break; + case KC_MS_BTN5: + register_button(true, MOUSE_BTN5); break; # endif default: @@ -422,15 +451,23 @@ void process_action(keyrecord_t *record, action_t action) { } else { mousekey_off(action.key.code); switch (action.key.code) { -# ifdef PS2_MOUSE_ENABLE +# if defined(PS2_MOUSE_ENABLE) || defined(POINTING_DEVICE_ENABLE) case KC_MS_BTN1: - tp_buttons &= ~(1 << 0); + register_button(false, MOUSE_BTN1); break; case KC_MS_BTN2: - tp_buttons &= ~(1 << 1); + register_button(false, MOUSE_BTN2); break; case KC_MS_BTN3: - tp_buttons &= ~(1 << 2); + register_button(false, MOUSE_BTN3); + break; +# endif +# ifdef POINTING_DEVICE_ENABLE + case KC_MS_BTN4: + register_button(false, MOUSE_BTN4); + break; + case KC_MS_BTN5: + register_button(false, MOUSE_BTN5); break; # endif default: @@ -692,20 +729,23 @@ void process_action(keyrecord_t *record, action_t action) { #endif #ifndef NO_ACTION_TAPPING -# ifdef RETRO_TAPPING +# if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) if (!is_tap_action(action)) { retro_tapping_counter = 0; } else { if (event.pressed) { if (tap_count > 0) { retro_tapping_counter = 0; - } else { } } else { if (tap_count > 0) { retro_tapping_counter = 0; } else { - if (retro_tapping_counter == 2) { + if ( +# ifdef RETRO_TAPPING_PER_KEY + get_retro_tapping(get_event_keycode(record->event, false), record) && +# endif + retro_tapping_counter == 2) { tap_code(action.layer_tap.code); } retro_tapping_counter = 0; diff --git a/tmk_core/common/action_util.c b/tmk_core/common/action_util.c index fd0e4409f0a3..000503b0821a 100644 --- a/tmk_core/common/action_util.c +++ b/tmk_core/common/action_util.c @@ -290,6 +290,32 @@ void set_macro_mods(uint8_t mods) { macro_mods = mods; } void clear_macro_mods(void) { macro_mods = 0; } #ifndef NO_ACTION_ONESHOT +/** \brief get oneshot mods + * + * FIXME: needs doc + */ +uint8_t get_oneshot_mods(void) { return oneshot_mods; } + +void add_oneshot_mods(uint8_t mods) { + if ((oneshot_mods & mods) != mods) { +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_time = timer_read(); +# endif + oneshot_mods |= mods; + oneshot_mods_changed_kb(mods); + } +} + +void del_oneshot_mods(uint8_t mods) { + if (oneshot_mods & mods) { + oneshot_mods &= ~mods; +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_time = oneshot_mods ? timer_read() : 0; +# endif + oneshot_mods_changed_kb(oneshot_mods); + } +} + /** \brief set oneshot mods * * FIXME: needs doc @@ -316,11 +342,6 @@ void clear_oneshot_mods(void) { oneshot_mods_changed_kb(oneshot_mods); } } -/** \brief get oneshot mods - * - * FIXME: needs doc - */ -uint8_t get_oneshot_mods(void) { return oneshot_mods; } #endif /** \brief Called when the one shot modifiers have been changed. diff --git a/tmk_core/common/action_util.h b/tmk_core/common/action_util.h index 5dd8393da48e..743ff1406b46 100644 --- a/tmk_core/common/action_util.h +++ b/tmk_core/common/action_util.h @@ -57,12 +57,11 @@ void set_macro_mods(uint8_t mods); void clear_macro_mods(void); /* oneshot modifier */ -void set_oneshot_mods(uint8_t mods); uint8_t get_oneshot_mods(void); +void add_oneshot_mods(uint8_t mods); +void del_oneshot_mods(uint8_t mods); +void set_oneshot_mods(uint8_t mods); void clear_oneshot_mods(void); -void oneshot_toggle(void); -void oneshot_enable(void); -void oneshot_disable(void); bool has_oneshot_mods_timed_out(void); uint8_t get_oneshot_locked_mods(void); diff --git a/tmk_core/common/arm_atsam/suspend.c b/tmk_core/common/arm_atsam/suspend.c index d1077be4c20c..e51426128d85 100644 --- a/tmk_core/common/arm_atsam/suspend.c +++ b/tmk_core/common/arm_atsam/suspend.c @@ -1,6 +1,6 @@ #include "matrix.h" #include "i2c_master.h" -#include "led_matrix.h" +#include "md_rgb_matrix.h" #include "suspend.h" /** \brief Suspend idle diff --git a/tmk_core/common/chibios/bootloader.c b/tmk_core/common/chibios/bootloader.c index 7b2cf5c435aa..f6d016ec9d13 100644 --- a/tmk_core/common/chibios/bootloader.c +++ b/tmk_core/common/chibios/bootloader.c @@ -32,7 +32,7 @@ extern uint32_t __ram0_end__; -void bootloader_jump(void) { +__attribute__((weak)) void bootloader_jump(void) { // For STM32 MCUs with dual-bank flash, and we're incapable of jumping to the bootloader. The first valid flash // bank is executed unconditionally after a reset, so it doesn't enter DFU unless BOOT0 is high. Instead, we do // it with hardware...in this case, we pull a GPIO high/low depending on the configuration, connects 3.3V to @@ -58,7 +58,7 @@ void enter_bootloader_mode_if_requested(void) {} // not needed at all, but if a extern uint32_t __ram0_end__; -void bootloader_jump(void) { +__attribute__((weak)) void bootloader_jump(void) { *MAGIC_ADDR = BOOTLOADER_MAGIC; // set magic flag => reset handler will jump into boot loader NVIC_SystemReset(); } @@ -85,8 +85,8 @@ void enter_bootloader_mode_if_requested(void) { # if defined(BOOTLOADER_KIIBOHD) /* Kiibohd Bootloader (MCHCK and Infinity KB) */ # define SCB_AIRCR_VECTKEY_WRITEMAGIC 0x05FA0000 -const uint8_t sys_reset_to_loader_magic[] = "\xff\x00\x7fRESET TO LOADER\x7f\x00\xff"; -void bootloader_jump(void) { +const uint8_t sys_reset_to_loader_magic[] = "\xff\x00\x7fRESET TO LOADER\x7f\x00\xff"; +__attribute__((weak)) void bootloader_jump(void) { __builtin_memcpy((void *)VBAT, (const void *)sys_reset_to_loader_magic, sizeof(sys_reset_to_loader_magic)); // request reset SCB->AIRCR = SCB_AIRCR_VECTKEY_WRITEMAGIC | SCB_AIRCR_SYSRESETREQ_Msk; @@ -95,7 +95,7 @@ void bootloader_jump(void) { # else /* defined(BOOTLOADER_KIIBOHD) */ /* Default for Kinetis - expecting an ARM Teensy */ # include "wait.h" -void bootloader_jump(void) { +__attribute__((weak)) void bootloader_jump(void) { wait_ms(100); __BKPT(0); } diff --git a/tmk_core/common/eeconfig.c b/tmk_core/common/eeconfig.c index fe56c57d1177..e15897552f11 100644 --- a/tmk_core/common/eeconfig.c +++ b/tmk_core/common/eeconfig.c @@ -13,6 +13,10 @@ # include "eeprom_driver.h" #endif +#if defined(HAPTIC_ENABLE) +# include "haptic.h" +#endif + /** \brief eeconfig enable * * FIXME: needs doc @@ -65,6 +69,15 @@ void eeconfig_init_quantum(void) { eeprom_update_byte(EECONFIG_HANDEDNESS, 0); #endif +#if defined(HAPTIC_ENABLE) + haptic_reset(); +#else + // this is used in case haptic is disabled, but we still want sane defaults + // in the haptic configuration eeprom. All zero will trigger a haptic_reset + // when a haptic-enabled firmware is loaded onto the keyboard. + eeprom_update_dword(EECONFIG_HAPTIC, 0); +#endif + eeconfig_init_kb(); } diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c index b0394af0035a..8c7bdc8b55f0 100644 --- a/tmk_core/common/keyboard.c +++ b/tmk_core/common/keyboard.c @@ -235,6 +235,20 @@ __attribute__((weak)) bool is_keyboard_left(void) { return true; } */ __attribute__((weak)) bool should_process_keypress(void) { return is_keyboard_master(); } +/** \brief housekeeping_task_kb + * + * Override this function if you have a need to execute code for every keyboard main loop iteration. + * This is specific to keyboard-level functionality. + */ +__attribute__((weak)) void housekeeping_task_kb(void) {} + +/** \brief housekeeping_task_user + * + * Override this function if you have a need to execute code for every keyboard main loop iteration. + * This is specific to user/keymap-level functionality. + */ +__attribute__((weak)) void housekeeping_task_user(void) {} + /** \brief keyboard_init * * FIXME: needs doc @@ -291,6 +305,10 @@ void keyboard_init(void) { dip_switch_init(); #endif +#if defined(DEBUG_MATRIX_SCAN_RATE) && defined(CONSOLE_ENABLE) + debug_enable = true; +#endif + keyboard_post_init_kb(); /* Always keep this last */ } @@ -315,6 +333,9 @@ void keyboard_task(void) { uint8_t keys_processed = 0; #endif + housekeeping_task_kb(); + housekeeping_task_user(); + #if defined(OLED_DRIVER_ENABLE) && !defined(OLED_DISABLE_TIMEOUT) uint8_t ret = matrix_scan(); #else diff --git a/tmk_core/common/keyboard.h b/tmk_core/common/keyboard.h index 79d9490898fa..caa18e7e7698 100644 --- a/tmk_core/common/keyboard.h +++ b/tmk_core/common/keyboard.h @@ -71,6 +71,9 @@ void keyboard_pre_init_user(void); void keyboard_post_init_kb(void); void keyboard_post_init_user(void); +void housekeeping_task_kb(void); +void housekeeping_task_user(void); + #ifdef __cplusplus } #endif diff --git a/tmk_core/common/matrix.h b/tmk_core/common/matrix.h index 78506059e6ac..31ec844302c8 100644 --- a/tmk_core/common/matrix.h +++ b/tmk_core/common/matrix.h @@ -30,16 +30,6 @@ typedef uint32_t matrix_row_t; # error "MATRIX_COLS: invalid value" #endif -#if (MATRIX_ROWS <= 8) -typedef uint8_t matrix_col_t; -#elif (MATRIX_ROWS <= 16) -typedef uint16_t matrix_col_t; -#elif (MATRIX_ROWS <= 32) -typedef uint32_t matrix_col_t; -#else -# error "MATRIX_ROWS: invalid value" -#endif - #define MATRIX_ROW_SHIFTER ((matrix_row_t)1) #define MATRIX_IS_ON(row, col) (matrix_get_row(row) && (1 << col)) diff --git a/tmk_core/common/report.h b/tmk_core/common/report.h index 1aa33c998d1e..610addf4319b 100644 --- a/tmk_core/common/report.h +++ b/tmk_core/common/report.h @@ -192,7 +192,11 @@ typedef struct { typedef struct { #if JOYSTICK_AXES_COUNT > 0 +# if JOYSTICK_AXES_RESOLUTION > 8 + int16_t axes[JOYSTICK_AXES_COUNT]; +# else int8_t axes[JOYSTICK_AXES_COUNT]; +# endif #endif #if JOYSTICK_BUTTON_COUNT > 0 diff --git a/tmk_core/protocol/arm_atsam.mk b/tmk_core/protocol/arm_atsam.mk index 8d6f724f0996..5bb45d658e51 100644 --- a/tmk_core/protocol/arm_atsam.mk +++ b/tmk_core/protocol/arm_atsam.mk @@ -4,9 +4,9 @@ SRC += $(ARM_ATSAM_DIR)/adc.c SRC += $(ARM_ATSAM_DIR)/clks.c SRC += $(ARM_ATSAM_DIR)/d51_util.c SRC += $(ARM_ATSAM_DIR)/i2c_master.c -ifeq ($(RGB_MATRIX_ENABLE),custom) - SRC += $(ARM_ATSAM_DIR)/led_matrix_programs.c - SRC += $(ARM_ATSAM_DIR)/led_matrix.c +ifeq ($(RGB_MATRIX_DRIVER),custom) + SRC += $(ARM_ATSAM_DIR)/md_rgb_matrix_programs.c + SRC += $(ARM_ATSAM_DIR)/md_rgb_matrix.c endif SRC += $(ARM_ATSAM_DIR)/main_arm_atsam.c SRC += $(ARM_ATSAM_DIR)/spi.c diff --git a/tmk_core/protocol/arm_atsam/arm_atsam_protocol.h b/tmk_core/protocol/arm_atsam/arm_atsam_protocol.h index 8cb00b872ae9..d126c66e7d11 100644 --- a/tmk_core/protocol/arm_atsam/arm_atsam_protocol.h +++ b/tmk_core/protocol/arm_atsam/arm_atsam_protocol.h @@ -35,7 +35,7 @@ along with this program. If not, see . # include "main_arm_atsam.h" # ifdef RGB_MATRIX_ENABLE -# include "led_matrix.h" +# include "md_rgb_matrix.h" # include "rgb_matrix.h" # endif # include "issi3733_driver.h" diff --git a/tmk_core/protocol/arm_atsam/main_arm_atsam.c b/tmk_core/protocol/arm_atsam/main_arm_atsam.c index e4e79d351044..e10be52fb809 100644 --- a/tmk_core/protocol/arm_atsam/main_arm_atsam.c +++ b/tmk_core/protocol/arm_atsam/main_arm_atsam.c @@ -305,6 +305,10 @@ int main(void) { // dprintf("5v=%u 5vu=%u dlow=%u dhi=%u gca=%u gcd=%u\r\n", v_5v, v_5v_avg, v_5v_avg - V5_LOW, v_5v_avg - V5_HIGH, gcr_actual, gcr_desired); } #endif // CONSOLE_ENABLE + + // Run housekeeping + housekeeping_task_kb(); + housekeeping_task_user(); } return 1; diff --git a/tmk_core/protocol/arm_atsam/led_matrix.c b/tmk_core/protocol/arm_atsam/md_rgb_matrix.c similarity index 98% rename from tmk_core/protocol/arm_atsam/led_matrix.c rename to tmk_core/protocol/arm_atsam/md_rgb_matrix.c index 69cb03a9f78b..439aaf8b3809 100644 --- a/tmk_core/protocol/arm_atsam/led_matrix.c +++ b/tmk_core/protocol/arm_atsam/md_rgb_matrix.c @@ -23,7 +23,7 @@ along with this program. If not, see . #ifdef USE_MASSDROP_CONFIGURATOR __attribute__((weak)) led_instruction_t led_instructions[] = {{.end = 1}}; -static void led_matrix_massdrop_config_override(int i); +static void md_rgb_matrix_config_override(int i); #endif // USE_MASSDROP_CONFIGURATOR void SERCOM1_0_Handler(void) { @@ -188,7 +188,7 @@ void issi3733_prepare_arrays(void) { } } -void led_matrix_prepare(void) { +void md_rgb_matrix_prepare(void) { for (uint8_t i = 0; i < ISSI3733_LED_COUNT; i++) { *led_map[i].rgb.r = 0; *led_map[i].rgb.g = 0; @@ -199,7 +199,7 @@ void led_matrix_prepare(void) { void led_set_one(int i, uint8_t r, uint8_t g, uint8_t b) { if (i < ISSI3733_LED_COUNT) { #ifdef USE_MASSDROP_CONFIGURATOR - led_matrix_massdrop_config_override(i); + md_rgb_matrix_config_override(i); #else led_buffer[i].r = r; led_buffer[i].g = g; @@ -219,7 +219,7 @@ void init(void) { issi3733_prepare_arrays(); - led_matrix_prepare(); + md_rgb_matrix_prepare(); gcr_min_counter = 0; v_5v_cat_hit = 0; @@ -290,7 +290,7 @@ void flush(void) { i2c_led_q_run(); } -void led_matrix_indicators(void) { +void md_rgb_matrix_indicators(void) { uint8_t kbled = keyboard_leds(); if (kbled && rgb_matrix_config.enable) { for (uint8_t i = 0; i < ISSI3733_LED_COUNT; i++) { @@ -397,7 +397,7 @@ static void led_run_pattern(led_setup_t* f, float* ro, float* go, float* bo, flo } } -static void led_matrix_massdrop_config_override(int i) { +static void md_rgb_matrix_config_override(int i) { float ro = 0; float go = 0; float bo = 0; diff --git a/tmk_core/protocol/arm_atsam/led_matrix.h b/tmk_core/protocol/arm_atsam/md_rgb_matrix.h similarity index 99% rename from tmk_core/protocol/arm_atsam/led_matrix.h rename to tmk_core/protocol/arm_atsam/md_rgb_matrix.h index 8eaa5623bda5..322b0f99d163 100644 --- a/tmk_core/protocol/arm_atsam/led_matrix.h +++ b/tmk_core/protocol/arm_atsam/md_rgb_matrix.h @@ -86,7 +86,7 @@ extern uint8_t gcr_actual_last; void gcr_compute(void); -void led_matrix_indicators(void); +void md_rgb_matrix_indicators(void); /*------------------------- Legacy Lighting Support ------------------------*/ diff --git a/tmk_core/protocol/arm_atsam/led_matrix_programs.c b/tmk_core/protocol/arm_atsam/md_rgb_matrix_programs.c similarity index 99% rename from tmk_core/protocol/arm_atsam/led_matrix_programs.c rename to tmk_core/protocol/arm_atsam/md_rgb_matrix_programs.c index 360102ba8455..b43008cc5b52 100644 --- a/tmk_core/protocol/arm_atsam/led_matrix_programs.c +++ b/tmk_core/protocol/arm_atsam/md_rgb_matrix_programs.c @@ -17,7 +17,7 @@ along with this program. If not, see . #ifdef USE_MASSDROP_CONFIGURATOR -# include "led_matrix.h" +# include "md_rgb_matrix.h" // Teal <-> Salmon led_setup_t leds_teal_salmon[] = { diff --git a/tmk_core/protocol/chibios/main.c b/tmk_core/protocol/chibios/main.c index a0d28f9afc08..400c0b8f5308 100644 --- a/tmk_core/protocol/chibios/main.c +++ b/tmk_core/protocol/chibios/main.c @@ -54,6 +54,9 @@ #ifdef STM32_EEPROM_ENABLE # include "eeprom_stm32.h" #endif +#ifdef EEPROM_DRIVER +# include "eeprom_driver.h" +#endif #include "suspend.h" #include "wait.h" @@ -150,6 +153,9 @@ int main(void) { #ifdef STM32_EEPROM_ENABLE EEPROM_Init(); #endif +#ifdef EEPROM_DRIVER + eeprom_driver_init(); +#endif // TESTING // chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL); @@ -230,6 +236,7 @@ int main(void) { /* Remote wakeup */ if (suspend_wakeup_condition()) { usbWakeupHost(&USB_DRIVER); + restart_usb_driver(&USB_DRIVER); } } /* Woken up */ @@ -258,5 +265,9 @@ int main(void) { #ifdef RAW_ENABLE raw_hid_task(); #endif + + // Run housekeeping + housekeeping_task_kb(); + housekeeping_task_user(); } } diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index ae33e86a70de..bb4bf6a58079 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -165,6 +165,19 @@ static const USBEndpointConfig shared_ep_config = { }; #endif +#if STM32_USB_USE_OTG1 +typedef struct { + size_t queue_capacity_in; + size_t queue_capacity_out; + USBInEndpointState in_ep_state; + USBOutEndpointState out_ep_state; + USBInEndpointState int_ep_state; + USBEndpointConfig inout_ep_config; + USBEndpointConfig int_ep_config; + const QMKUSBConfig config; + QMKUSBDriver driver; +} usb_driver_config_t; +#else typedef struct { size_t queue_capacity_in; size_t queue_capacity_out; @@ -177,7 +190,54 @@ typedef struct { const QMKUSBConfig config; QMKUSBDriver driver; } usb_driver_config_t; +#endif +#if STM32_USB_USE_OTG1 +/* Reusable initialization structure - see USBEndpointConfig comment at top of file */ +#define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) \ + { \ + .queue_capacity_in = stream##_IN_CAPACITY, .queue_capacity_out = stream##_OUT_CAPACITY, \ + .inout_ep_config = \ + { \ + stream##_IN_MODE, /* Interrupt EP */ \ + NULL, /* SETUP packet notification callback */ \ + qmkusbDataTransmitted, /* IN notification callback */ \ + qmkusbDataReceived, /* OUT notification callback */ \ + stream##_EPSIZE, /* IN maximum packet size */ \ + stream##_EPSIZE, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + 2, /* IN multiplier */ \ + NULL /* SETUP buffer (not a SETUP endpoint) */ \ + }, \ + .int_ep_config = \ + { \ + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ \ + NULL, /* SETUP packet notification callback */ \ + qmkusbInterruptTransmitted, /* IN notification callback */ \ + NULL, /* OUT notification callback */ \ + CDC_NOTIFICATION_EPSIZE, /* IN maximum packet size */ \ + 0, /* OUT maximum packet size */ \ + NULL, /* IN Endpoint state */ \ + NULL, /* OUT endpoint state */ \ + 2, /* IN multiplier */ \ + NULL, /* SETUP buffer (not a SETUP endpoint) */ \ + }, \ + .config = { \ + .usbp = &USB_DRIVER, \ + .bulk_in = stream##_IN_EPNUM, \ + .bulk_out = stream##_OUT_EPNUM, \ + .int_in = notification, \ + .in_buffers = stream##_IN_CAPACITY, \ + .out_buffers = stream##_OUT_CAPACITY, \ + .in_size = stream##_EPSIZE, \ + .out_size = stream##_EPSIZE, \ + .fixed_size = fixedsize, \ + .ib = (__attribute__((aligned(4))) uint8_t[BQ_BUFFER_SIZE(stream##_IN_CAPACITY, stream##_EPSIZE)]){}, \ + .ob = (__attribute__((aligned(4))) uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ + } \ + } +#else /* Reusable initialization structure - see USBEndpointConfig comment at top of file */ #define QMK_USB_DRIVER_CONFIG(stream, notification, fixedsize) \ { \ @@ -235,6 +295,7 @@ typedef struct { .ob = (__attribute__((aligned(4))) uint8_t[BQ_BUFFER_SIZE(stream##_OUT_CAPACITY, stream##_EPSIZE)]){}, \ } \ } +#endif typedef struct { union { @@ -327,8 +388,12 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) { usbInitEndpointI(usbp, SHARED_IN_EPNUM, &shared_ep_config); #endif for (int i = 0; i < NUM_USB_DRIVERS; i++) { + #if STM32_USB_USE_OTG1 + usbInitEndpointI(usbp, drivers.array[i].config.bulk_in, &drivers.array[i].inout_ep_config); + #else usbInitEndpointI(usbp, drivers.array[i].config.bulk_in, &drivers.array[i].in_ep_config); usbInitEndpointI(usbp, drivers.array[i].config.bulk_out, &drivers.array[i].out_ep_config); + #endif if (drivers.array[i].config.int_in) { usbInitEndpointI(usbp, drivers.array[i].config.int_in, &drivers.array[i].int_ep_config); } @@ -553,12 +618,21 @@ static const USBConfig usbcfg = { */ void init_usb_driver(USBDriver *usbp) { for (int i = 0; i < NUM_USB_DRIVERS; i++) { + #if STM32_USB_USE_OTG1 + QMKUSBDriver *driver = &drivers.array[i].driver; + drivers.array[i].inout_ep_config.in_state = &drivers.array[i].in_ep_state; + drivers.array[i].inout_ep_config.out_state = &drivers.array[i].out_ep_state; + drivers.array[i].int_ep_config.in_state = &drivers.array[i].int_ep_state; + qmkusbObjectInit(driver, &drivers.array[i].config); + qmkusbStart(driver, &drivers.array[i].config); + #else QMKUSBDriver *driver = &drivers.array[i].driver; drivers.array[i].in_ep_config.in_state = &drivers.array[i].in_ep_state; drivers.array[i].out_ep_config.out_state = &drivers.array[i].out_ep_state; drivers.array[i].int_ep_config.in_state = &drivers.array[i].int_ep_state; qmkusbObjectInit(driver, &drivers.array[i].config); qmkusbStart(driver, &drivers.array[i].config); + #endif } /* @@ -574,6 +648,13 @@ void init_usb_driver(USBDriver *usbp) { chVTObjectInit(&keyboard_idle_timer); } +void restart_usb_driver(USBDriver *usbp) { + usbStop(usbp); + usbDisconnectBus(usbp); + usbStart(usbp, &usbcfg); + usbConnectBus(usbp); +} + /* --------------------------------------------------------- * Keyboard functions * --------------------------------------------------------- diff --git a/tmk_core/protocol/chibios/usb_main.h b/tmk_core/protocol/chibios/usb_main.h index 94baf9b35e06..d8813f480134 100644 --- a/tmk_core/protocol/chibios/usb_main.h +++ b/tmk_core/protocol/chibios/usb_main.h @@ -35,6 +35,9 @@ /* Initialize the USB driver and bus */ void init_usb_driver(USBDriver *usbp); +/* Restart the USB driver and bus */ +void restart_usb_driver(USBDriver *usbp); + /* --------------- * Keyboard header * --------------- diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c index cec00440269a..878be7d3424b 100644 --- a/tmk_core/protocol/lufa/lufa.c +++ b/tmk_core/protocol/lufa/lufa.c @@ -1104,6 +1104,10 @@ int main(void) { #if !defined(INTERRUPT_CONTROL_ENDPOINT) USB_USBTask(); #endif + + // Run housekeeping + housekeeping_task_kb(); + housekeeping_task_user(); } } diff --git a/tmk_core/protocol/usb_descriptor.c b/tmk_core/protocol/usb_descriptor.c index f5d32445de50..a0c0d4cdc80f 100644 --- a/tmk_core/protocol/usb_descriptor.c +++ b/tmk_core/protocol/usb_descriptor.c @@ -41,6 +41,10 @@ #include "usb_descriptor.h" #include "usb_descriptor_common.h" +#ifdef JOYSTICK_ENABLE +# include "joystick.h" +#endif + // clang-format off /* @@ -308,10 +312,17 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM JoystickReport[] = { HID_RI_USAGE(8, 0x35), // Rz # endif # if JOYSTICK_AXES_COUNT >= 1 - HID_RI_LOGICAL_MINIMUM(8, -127), - HID_RI_LOGICAL_MAXIMUM(8, 127), + # if JOYSTICK_AXES_RESOLUTION == 8 + HID_RI_LOGICAL_MINIMUM(8, -JOYSTICK_RESOLUTION), + HID_RI_LOGICAL_MAXIMUM(8, JOYSTICK_RESOLUTION), HID_RI_REPORT_COUNT(8, JOYSTICK_AXES_COUNT), HID_RI_REPORT_SIZE(8, 0x08), + # else + HID_RI_LOGICAL_MINIMUM(16, -JOYSTICK_RESOLUTION), + HID_RI_LOGICAL_MAXIMUM(16, JOYSTICK_RESOLUTION), + HID_RI_REPORT_COUNT(8, JOYSTICK_AXES_COUNT), + HID_RI_REPORT_SIZE(8, 0x10), + # endif HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), # endif diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h index 02a4b1ce5923..aa8863f43fb3 100644 --- a/tmk_core/protocol/usb_descriptor.h +++ b/tmk_core/protocol/usb_descriptor.h @@ -205,7 +205,11 @@ enum usb_endpoints { #ifdef RAW_ENABLE RAW_IN_EPNUM = NEXT_EPNUM, + #if STM32_USB_USE_OTG1 + #define RAW_OUT_EPNUM RAW_IN_EPNUM + #else RAW_OUT_EPNUM = NEXT_EPNUM, + #endif #endif #ifdef SHARED_EP_ENABLE @@ -219,7 +223,11 @@ enum usb_endpoints { // ChibiOS has enough memory and descriptor to actually enable the endpoint // It could use the same endpoint numbers, as that's supported by ChibiOS // But the QMK code currently assumes that the endpoint numbers are different + #if STM32_USB_USE_OTG1 + #define CONSOLE_OUT_EPNUM CONSOLE_IN_EPNUM + #else CONSOLE_OUT_EPNUM = NEXT_EPNUM, + #endif # else # define CONSOLE_OUT_EPNUM CONSOLE_IN_EPNUM # endif @@ -227,17 +235,29 @@ enum usb_endpoints { #ifdef MIDI_ENABLE MIDI_STREAM_IN_EPNUM = NEXT_EPNUM, + #if STM32_USB_USE_OTG1 + #define MIDI_STREAM_OUT_EPNUM MIDI_STREAM_IN_EPNUM + #else MIDI_STREAM_OUT_EPNUM = NEXT_EPNUM, + #endif #endif #ifdef VIRTSER_ENABLE CDC_NOTIFICATION_EPNUM = NEXT_EPNUM, CDC_IN_EPNUM = NEXT_EPNUM, + #if STM32_USB_USE_OTG1 + #define CDC_OUT_EPNUM CDC_IN_EPNUM + #else CDC_OUT_EPNUM = NEXT_EPNUM, + #endif #endif #ifdef JOYSTICK_ENABLE JOYSTICK_IN_EPNUM = NEXT_EPNUM, + #if STM32_USB_USE_OTG1 + JOYSTICK_OUT_EPNUM = JOYSTICK_IN_EPNUM, + #else JOYSTICK_OUT_EPNUM = NEXT_EPNUM, + #endif #endif }; diff --git a/tmk_core/protocol/vusb/main.c b/tmk_core/protocol/vusb/main.c index a57df5ce06a7..0e3447d926d1 100644 --- a/tmk_core/protocol/vusb/main.c +++ b/tmk_core/protocol/vusb/main.c @@ -153,6 +153,10 @@ int main(void) { console_task(); } #endif + + // Run housekeeping + housekeeping_task_kb(); + housekeeping_task_user(); } else if (suspend_wakeup_condition()) { usb_remote_wakeup(); } diff --git a/users/tominabox1/rules.mk b/users/tominabox1/rules.mk index b70b128cf3e5..22b6a858f4b3 100644 --- a/users/tominabox1/rules.mk +++ b/users/tominabox1/rules.mk @@ -8,7 +8,7 @@ NKRO_ENABLE = yes BOOTMAGIC_ENABLE = full # Virtual DIP switch configuration ifeq ($(strip $(KEYBOARD)), crkbd/rev1) -RGB_MATRIX_ENABLE = WS2812 +RGB_MATRIX_ENABLE = yes EXTRAFLAGS += -flto BOOTLOADER = qmk-dfu OLED_DRIVER_ENABLE = yes @@ -19,7 +19,6 @@ MOUSEKEY_ENABLE = no # Mouse keys EXTRAKEY_ENABLE = yes # Audio control and System control BACKLIGHT_ENABLE = yes # Enable keyboard backlight functionality on B7 by default RGBLIGHT_ENABLE = yes # Enable keyboard RGB underglow -# RGB_MATRIX_ENABLE = WS2812 endif diff --git a/users/yanfali/config.h b/users/yanfali/config.h index 2c427f033178..26517a9f64b0 100644 --- a/users/yanfali/config.h +++ b/users/yanfali/config.h @@ -1,6 +1,6 @@ #pragma once -#ifdef KEYBOARD_maartenwut_plain60 +#ifdef KEYBOARD_evyd13_plain60 #define B7_AUDIO #ifdef AUDIO_ENABLE diff --git a/users/yanfali/rules.mk b/users/yanfali/rules.mk index a7208daa2c5a..bf88c045280a 100644 --- a/users/yanfali/rules.mk +++ b/users/yanfali/rules.mk @@ -6,7 +6,7 @@ LTO_ENABLE = yes AUDIO_ENABLE = no # only enable audio on specific boards -ifeq ($(strip $(KEYBOARD)), maartenwut/plain60) +ifeq ($(strip $(KEYBOARD)), evyd13/plain60) AUDIO_ENABLE = yes endif