summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/_summary.md2
-rw-r--r--docs/breaking_changes.md4
-rw-r--r--docs/chibios_upgrade_instructions.md56
-rw-r--r--docs/cli_commands.md2
-rw-r--r--docs/compatible_microcontrollers.md2
-rw-r--r--docs/config_options.md33
-rw-r--r--docs/custom_quantum_functions.md8
-rw-r--r--docs/eeprom_driver.md3
-rw-r--r--docs/feature_debounce_type.md4
-rw-r--r--docs/feature_dip_switch.md16
-rw-r--r--docs/feature_haptic_feedback.md26
-rw-r--r--docs/feature_key_overrides.md229
-rw-r--r--docs/feature_oled_driver.md4
-rw-r--r--docs/feature_rgb_matrix.md141
-rw-r--r--docs/feature_rgblight.md47
-rw-r--r--docs/feature_split_keyboard.md122
-rw-r--r--docs/feature_st7565.md274
-rw-r--r--docs/ja/compatible_microcontrollers.md2
-rw-r--r--docs/ja/feature_dip_switch.md16
-rw-r--r--docs/serial_driver.md15
-rw-r--r--docs/syllabus.md1
-rw-r--r--docs/understanding_qmk.md1
-rw-r--r--docs/zh-cn/custom_quantum_functions.md6
23 files changed, 924 insertions, 90 deletions
diff --git a/docs/_summary.md b/docs/_summary.md
index 9798ef5127..6c39aeda09 100644
--- a/docs/_summary.md
+++ b/docs/_summary.md
@@ -77,6 +77,7 @@
* [Combos](feature_combo.md)
* [Debounce API](feature_debounce_type.md)
* [Key Lock](feature_key_lock.md)
+ * [Key Overrides](feature_key_overrides.md)
* [Layers](feature_layers.md)
* [One Shot Keys](one_shot_keys.md)
* [Pointing Device](feature_pointing_device.md)
@@ -93,6 +94,7 @@
* Hardware Features
* Displays
* [HD44780 LCD Controller](feature_hd44780.md)
+ * [ST7565 LCD Driver](feature_st7565.md)
* [OLED Driver](feature_oled_driver.md)
* Lighting
* [Backlight](feature_backlight.md)
diff --git a/docs/breaking_changes.md b/docs/breaking_changes.md
index b0d56a81bd..a1a56bd457 100644
--- a/docs/breaking_changes.md
+++ b/docs/breaking_changes.md
@@ -100,3 +100,7 @@ This happens immediately after the previous `develop` branch is merged.
* [ ] `git pull --ff-only`
* [ ] `git merge --no-ff develop`
* [ ] `git push upstream master`
+
+## Post-merge operations
+
+* (Optional) [update ChibiOS + ChibiOS-Contrib on `develop`](chibios_upgrade_instructions.md)
diff --git a/docs/chibios_upgrade_instructions.md b/docs/chibios_upgrade_instructions.md
new file mode 100644
index 0000000000..40c2faafcf
--- /dev/null
+++ b/docs/chibios_upgrade_instructions.md
@@ -0,0 +1,56 @@
+# ChibiOS Upgrade Procedure
+
+ChibiOS and ChibiOS-Contrib need to be updated in tandem -- the latter has a branch tied to the ChibiOS version in use and should not be mixed with different versions.
+
+## Getting ChibiOS
+
+* `svn` Initialisation:
+ * Only needed to be done once
+ * You might need to separately install `git-svn` package in your OS's package manager
+ * `git svn init --stdlayout --prefix='svn/' http://svn.osdn.net/svnroot/chibios/`
+ * `git remote add qmk git@github.com:qmk/ChibiOS.git`
+* Updating:
+ * `git svn fetch`
+ * First time around this will take several hours
+ * Subsequent updates will be incremental only
+* Tagging example (work out which version first!):
+ * `git tag -a ver20.3.3 -m ver20.3.3 svn/tags/ver20.3.3`
+ * `git push qmk ver20.3.3`
+ * `git tag -a breaking_YYYY_qN -m breaking_YYYY_qN svn/tags/ver20.3.3`
+ * `git push qmk breaking_YYYY_qN`
+
+## Getting ChibiOS-Contrib
+
+* `git` Initialisation:
+ * `git clone git@github.com:qmk/ChibiOS-Contrib`
+ * `git remote add upstream https://github.com/ChibiOS/ChibiOS-Contrib`
+ * `git checkout -b chibios-20.3.x upstream/chibios-20.3.x`
+* Updating:
+ * `git fetch --all --tags --prune`
+ * `git checkout chibios-20.3.x`
+ * `git pull --ff-only`
+ * `git push origin chibios-20.3.x`
+ * `git tag -a breaking_YYYY_qN -m breaking_YYYY_qN chibios-20.3.x`
+ * `git push origin breaking_YYYY_qN`
+
+## Updating submodules
+
+* Update the submodules
+ * `cd $QMK_FIRMWARE`
+ * `git checkout develop`
+ * `git pull --ff-only`
+ * `git checkout -b chibios-version-bump`
+ * `cd lib/chibios`
+ * `git fetch --all --tags --prune`
+ * `git checkout breaking_YYYY_qN`
+ * `cd ../chibios-contrib`
+ * `git fetch --all --tags --prune`
+ * `git checkout breaking_YYYY_qN`
+* Build everything
+ * `cd $QMK_FIRMWARE`
+ * `qmk multibuild -j4`
+ * Make sure there are no errors
+* Push to the repo
+ * `git commit -am 'Update ChibiOS to XXXXXXXXX'`
+ * `git push --set-upstream origin chibios-version-bump`
+* Make a PR to qmk_firmware with the new branch \ No newline at end of file
diff --git a/docs/cli_commands.md b/docs/cli_commands.md
index 581342093a..e30593daa9 100644
--- a/docs/cli_commands.md
+++ b/docs/cli_commands.md
@@ -368,7 +368,7 @@ qmk generate-docs
## `qmk generate-rgb-breathe-table`
-This command generates a lookup table (LUT) header file for the [RGB Lighting](feature_rgblight.md) feature's breathing animation. Place this file in your keyboard or keymap directory as `rgblight_breathe_table.h` to override the default LUT in `quantum/`.
+This command generates a lookup table (LUT) header file for the [RGB Lighting](feature_rgblight.md) feature's breathing animation. Place this file in your keyboard or keymap directory as `rgblight_breathe_table.h` to override the default LUT in `quantum/rgblight/`.
**Usage**:
diff --git a/docs/compatible_microcontrollers.md b/docs/compatible_microcontrollers.md
index 0f5b140de0..865b29feec 100644
--- a/docs/compatible_microcontrollers.md
+++ b/docs/compatible_microcontrollers.md
@@ -31,6 +31,8 @@ You can also use any ARM chip with USB that [ChibiOS](https://www.chibios.org) s
* [STM32F446](https://www.st.com/en/microcontrollers-microprocessors/stm32f446.html)
* [STM32G431](https://www.st.com/en/microcontrollers-microprocessors/stm32g4x1.html)
* [STM32G474](https://www.st.com/en/microcontrollers-microprocessors/stm32g4x4.html)
+ * [STM32L412](https://www.st.com/en/microcontrollers-microprocessors/stm32l4x2.html)
+ * [STM32L422](https://www.st.com/en/microcontrollers-microprocessors/stm32l4x2.html)
* [STM32L433](https://www.st.com/en/microcontrollers-microprocessors/stm32l4x3.html)
* [STM32L443](https://www.st.com/en/microcontrollers-microprocessors/stm32l4x3.html)
diff --git a/docs/config_options.md b/docs/config_options.md
index d0f0b316e0..0c98b31010 100644
--- a/docs/config_options.md
+++ b/docs/config_options.md
@@ -51,8 +51,10 @@ This is a C header file that is one of the first things included, and will persi
* the number of columns in your keyboard's matrix
* `#define MATRIX_ROW_PINS { D0, D5, B5, B6 }`
* pins of the rows, from top to bottom
+ * may be omitted by the keyboard designer if matrix reads are handled in an alternate manner. See [low-level matrix overrides](custom_quantum_functions.md?id=low-level-matrix-overrides) for more information.
* `#define MATRIX_COL_PINS { F1, F0, B0, C7, F4, F5, F6, F7, D4, D6, B4, D7 }`
* pins of the columns, from left to right
+ * may be omitted by the keyboard designer if matrix reads are handled in an alternate manner. See [low-level matrix overrides](custom_quantum_functions.md?id=low-level-matrix-overrides) for more information.
* `#define MATRIX_IO_DELAY 30`
* the delay in microseconds when between changing matrix pin state and reading values
* `#define UNUSED_PINS { D1, D2, D3, B1, B2, B3 }`
@@ -193,6 +195,8 @@ If you define these options you will enable the associated feature, which may in
* Sets the delay between `register_code` and `unregister_code`, if you're having issues with it registering properly (common on VUSB boards). The value is in milliseconds.
* `#define TAP_HOLD_CAPS_DELAY 80`
* Sets the delay for Tap Hold keys (`LT`, `MT`) when using `KC_CAPSLOCK` keycode, as this has some special handling on MacOS. The value is in milliseconds, and defaults to 80 ms if not defined. For macOS, you may want to set this to 200 or higher.
+* `#define KEY_OVERRIDE_REPEAT_DELAY 500`
+ * Sets the key repeat interval for [key overrides](feature_key_overrides.md).
## RGB Light Configuration
@@ -272,7 +276,7 @@ There are a few different ways to set handedness for split keyboards (listed in
### Other Options
* `#define USE_I2C`
- * For using I2C instead of Serial (defaults to serial)
+ * For using I2C instead of Serial (default is serial; serial transport is supported on ARM -- I2C is AVR-only)
* `#define SOFT_SERIAL_PIN D0`
* When using serial, define this. `D0` or `D1`,`D2`,`D3`,`E6`.
@@ -280,6 +284,7 @@ There are a few different ways to set handedness for split keyboards (listed in
* `#define MATRIX_ROW_PINS_RIGHT { <row pins> }`
* `#define MATRIX_COL_PINS_RIGHT { <col pins> }`
* If you want to specify a different pinout for the right half than the left half, you can define `MATRIX_ROW_PINS_RIGHT`/`MATRIX_COL_PINS_RIGHT`. Currently, the size of `MATRIX_ROW_PINS` must be the same as `MATRIX_ROW_PINS_RIGHT` and likewise for the definition of columns.
+ * may be omitted by the keyboard designer if matrix reads are handled in an alternate manner. See [low-level matrix overrides](custom_quantum_functions.md?id=low-level-matrix-overrides) for more information.
* `#define DIRECT_PINS_RIGHT { { F1, F0, B0, C7 }, { F4, F5, F6, F7 } }`
* If you want to specify a different direct pinout for the right half than the left half, you can define `DIRECT_PINS_RIGHT`. Currently, the size of `DIRECT_PINS` must be the same as `DIRECT_PINS_RIGHT`.
@@ -300,7 +305,7 @@ There are a few different ways to set handedness for split keyboards (listed in
* `#define SPLIT_USB_DETECT`
* Detect (with timeout) USB connection when delegating master/slave
* Default behavior for ARM
- * Required for AVR Teensy
+ * Required for AVR Teensy (without hardware mods)
* `#define SPLIT_USB_TIMEOUT 2000`
* Maximum timeout when detecting master/slave when using `SPLIT_USB_DETECT`
@@ -308,6 +313,28 @@ There are a few different ways to set handedness for split keyboards (listed in
* `#define SPLIT_USB_TIMEOUT_POLL 10`
* Poll frequency when detecting master/slave when using `SPLIT_USB_DETECT`
+* `#define FORCED_SYNC_THROTTLE_MS 100`
+ * Deadline for synchronizing data from master to slave when using the QMK-provided split transport.
+
+* `#define SPLIT_TRANSPORT_MIRROR`
+ * Mirrors the master-side matrix on the slave when using the QMK-provided split transport.
+
+* `#define SPLIT_LAYER_STATE_ENABLE`
+ * Ensures the current layer state is available on the slave when using the QMK-provided split transport.
+
+* `#define SPLIT_LED_STATE_ENABLE`
+ * Ensures the current host indicator state (caps/num/scroll) is available on the slave when using the QMK-provided split transport.
+
+* `#define SPLIT_MODS_ENABLE`
+ * Ensures the current modifier state (normal, weak, and oneshot) is available on the slave when using the QMK-provided split transport.
+
+* `#define SPLIT_WPM_ENABLE`
+ * Ensures the current WPM is available on the slave when using the QMK-provided split transport.
+
+* `#define SPLIT_TRANSACTION_IDS_KB .....`
+* `#define SPLIT_TRANSACTION_IDS_USER .....`
+ * Allows for custom data sync with the slave when using the QMK-provided split transport. See [custom data sync between sides](feature_split_keyboard.md#custom-data-sync) for more information.
+
# The `rules.mk` File
This is a [make](https://www.gnu.org/software/make/manual/make.html) file that is included by the top-level `Makefile`. It is used to set some information about the MCU that we will be compiling for as well as enabling and disabling certain features.
@@ -375,6 +402,8 @@ Use these to enable or disable building certain features. The more you have enab
* USB N-Key Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
* `AUDIO_ENABLE`
* Enable the audio subsystem.
+* `KEY_OVERRIDE_ENABLE`
+ * Enable the key override feature
* `RGBLIGHT_ENABLE`
* Enable keyboard underlight functionality
* `LEADER_ENABLE`
diff --git a/docs/custom_quantum_functions.md b/docs/custom_quantum_functions.md
index 694b421e79..30c637bb49 100644
--- a/docs/custom_quantum_functions.md
+++ b/docs/custom_quantum_functions.md
@@ -144,6 +144,14 @@ This is useful for setting up stuff that you may need elsewhere, but isn't hardw
* Keyboard/Revision: `void matrix_init_kb(void)`
* Keymap: `void matrix_init_user(void)`
+### Low-level Matrix Overrides Function Documentation :id=low-level-matrix-overrides
+
+* GPIO pin initialisation: `void matrix_init_pins(void)`
+ * This needs to perform the low-level initialisation of all row and column pins. By default this will initialise the input/output state of each of the GPIO pins listed in `MATRIX_ROW_PINS` and `MATRIX_COL_PINS`, based on whether or not the keyboard is set up for `ROW2COL`, `COL2ROW`, or `DIRECT_PINS`. Should the keyboard designer override this function, no initialisation of pin state will occur within QMK itself, instead deferring to the keyboard's override.
+* `COL2ROW`-based row reads: `void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col)`
+* `ROW2COL`-based column reads: `void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)`
+* `DIRECT_PINS`-based reads: `void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)`
+ * These three functions need to perform the low-level retrieval of matrix state of relevant input pins, based on the matrix type. Only one of the functions should be implemented, if needed. By default this will iterate through `MATRIX_ROW_PINS` and `MATRIX_COL_PINS`, configuring the inputs and outputs based on whether or not the keyboard is set up for `ROW2COL`, `COL2ROW`, or `DIRECT_PINS`. Should the keyboard designer override this function, no manipulation of matrix GPIO pin state will occur within QMK itself, instead deferring to the keyboard's override.
## Keyboard Post Initialization code
diff --git a/docs/eeprom_driver.md b/docs/eeprom_driver.md
index e2c262546d..6dcf10c04d 100644
--- a/docs/eeprom_driver.md
+++ b/docs/eeprom_driver.md
@@ -31,6 +31,9 @@ Currently QMK supports 24xx-series chips over I2C. As such, requires a working i
`#define EXTERNAL_EEPROM_PAGE_SIZE` | Page size of the EEPROM in bytes, as specified in the datasheet | 32
`#define EXTERNAL_EEPROM_ADDRESS_SIZE` | The number of bytes to transmit for the memory location within the EEPROM | 2
`#define EXTERNAL_EEPROM_WRITE_TIME` | Write cycle time of the EEPROM, as specified in the datasheet | 5
+`#define EXTERNAL_EEPROM_WP_PIN` | If defined the WP pin will be toggled appropriately when writing to the EEPROM. | _none_
+
+Some I2C EEPROM manufacturers explicitly recommend against hardcoding the WP pin to ground. This is in order to protect the eeprom memory content during power-up/power-down/brown-out conditions at low voltage where the eeprom is still operational, but the i2c master output might be unpredictable. If a WP pin is configured, then having an external pull-up on the WP pin is recommended.
Default values and extended descriptions can be found in `drivers/eeprom/eeprom_i2c.h`.
diff --git a/docs/feature_debounce_type.md b/docs/feature_debounce_type.md
index 3ad74224c1..306185fe83 100644
--- a/docs/feature_debounce_type.md
+++ b/docs/feature_debounce_type.md
@@ -121,16 +121,16 @@ DEBOUNCE_TYPE = <name of algorithm>
Where name of algorithm is one of:
* ```sym_defer_g``` - debouncing per keyboard. On any state change, a global timer is set. When ```DEBOUNCE``` milliseconds of no changes has occurred, all input changes are pushed.
* This is the current default algorithm. This is the highest performance algorithm with lowest memory usage, and it's also noise-resistant.
-* ```sym_eager_pr``` - debouncing per row. On any state change, response is immediate, followed by locking the row ```DEBOUNCE``` milliseconds of no further input for that row.
+* ```sym_eager_pr``` - debouncing per row. On any state change, response is immediate, followed by locking the row ```DEBOUNCE``` milliseconds of no further input for that row.
For use in keyboards where refreshing ```NUM_KEYS``` 8-bit counters is computationally expensive / low scan rate, and fingers usually only hit one row at a time. This could be
appropriate for the ErgoDox models; the matrix is rotated 90°, and hence its "rows" are really columns, and each finger only hits a single "row" at a time in normal use.
* ```sym_eager_pk``` - debouncing per key. On any state change, response is immediate, followed by ```DEBOUNCE``` milliseconds of no further input for that key
* ```sym_defer_pk``` - debouncing per key. On any state change, a per-key timer is set. When ```DEBOUNCE``` milliseconds of no changes have occurred on that key, the key status change is pushed.
+* ```asym_eager_defer_pk``` - debouncing per key. On a key-down state change, response is immediate, followed by ```DEBOUNCE``` milliseconds of no further input for that key. On a key-up state change, a per-key timer is set. When ```DEBOUNCE``` milliseconds of no changes have occurred on that key, the key-up status change is pushed.
### A couple algorithms that could be implemented in the future:
* ```sym_defer_pr```
* ```sym_eager_g```
-* ```asym_eager_defer_pk```
### Use your own debouncing code
You have the option to implement you own debouncing algorithm. To do this:
diff --git a/docs/feature_dip_switch.md b/docs/feature_dip_switch.md
index 15e449c4c4..5e8c19bfa7 100644
--- a/docs/feature_dip_switch.md
+++ b/docs/feature_dip_switch.md
@@ -23,8 +23,9 @@ or
The callback functions can be inserted into your `<keyboard>.c`:
```c
-void dip_switch_update_kb(uint8_t index, bool active) {
- dip_switch_update_user(index, active);
+bool dip_switch_update_kb(uint8_t index, bool active) {
+ if !(dip_switch_update_user(index, active)) { return false; }
+ return true;
}
```
@@ -32,7 +33,7 @@ void dip_switch_update_kb(uint8_t index, bool active) {
or `keymap.c`:
```c
-void dip_switch_update_user(uint8_t index, bool active) {
+bool dip_switch_update_user(uint8_t index, bool active) {
switch (index) {
case 0:
if(active) { audio_on(); } else { audio_off(); }
@@ -57,6 +58,7 @@ void dip_switch_update_user(uint8_t index, bool active) {
}
break;
}
+ return true;
}
```
@@ -64,8 +66,9 @@ Additionally, we support bit mask functions which allow for more complex handlin
```c
-void dip_switch_update_mask_kb(uint32_t state) {
- dip_switch_update_mask_user(state);
+bool dip_switch_update_mask_kb(uint32_t state) {
+ if (!dip_switch_update_mask_user(state)) { return false; }
+ return true;
}
```
@@ -73,7 +76,7 @@ void dip_switch_update_mask_kb(uint32_t state) {
or `keymap.c`:
```c
-void dip_switch_update_mask_user(uint32_t state) {
+bool dip_switch_update_mask_user(uint32_t state) {
if (state & (1UL<<0) && state & (1UL<<1)) {
layer_on(_ADJUST); // C on esc
} else {
@@ -89,6 +92,7 @@ void dip_switch_update_mask_user(uint32_t state) {
} else {
layer_off(_TEST_B);
}
+ return true;
}
```
diff --git a/docs/feature_haptic_feedback.md b/docs/feature_haptic_feedback.md
index a092e784c7..469c9c7981 100644
--- a/docs/feature_haptic_feedback.md
+++ b/docs/feature_haptic_feedback.md
@@ -162,4 +162,28 @@ This will set what sequence HPT_RST will set as the active mode. If not defined,
### DRV2605L Continuous Haptic Mode
-This mode sets continuous haptic feedback with the option to increase or decrease strength.
+This mode sets continuous haptic feedback with the option to increase or decrease strength.
+
+## Haptic Key Exclusion
+The Haptic Exclusion is implemented as `__attribute__((weak)) bool get_haptic_enabled_key(uint16_t keycode, keyrecord_t *record)` in haptic.c. This allows a re-definition at the required level with the specific requirement / exclusion.
+
+### NO_HAPTIC_MOD
+With the entry of `#define NO_HAPTIC_MOD` in config.h, modifiers from Left Control to Right GUI will not trigger a feedback. This also includes modifiers in a Mod Tap configuration.
+
+### NO_HAPTIC_FN
+With the entry of `#define NO_HAPTIC_FN` in config.h, layer keys will not rigger a feedback.
+
+### NO_HAPTIC_ALPHA
+With the entry of `#define NO_HAPTIC_ALPHA` in config.h, none of the alpha keys (A ... Z) will trigger a feedback.
+
+### NO_HAPTIC_PUNCTUATION
+With the entry of `#define NO_HAPTIC_PUNCTUATION` in config.h, none of the following keys will trigger a feedback: Enter, ESC, Backspace, Space, Minus, Equal, Left Bracket, Right Bracket, Backslash, Non-US Hash, Semicolon, Quote, Grave, Comma, Slash, Dot, Non-US Backslash.
+
+### NO_HAPTIC_LOCKKEYS
+With the entry of `#define NO_HAPTIC_LOCKKEYS` in config.h, none of the following keys will trigger a feedback: Caps Lock, Scroll Lock, Num Lock.
+
+### NO_HAPTIC_NAV
+With the entry of `#define NO_HAPTIC_NAV` in config.h, none of the following keys will trigger a feedback: Print Screen, Pause, Insert, Delete, Page Down, Page Up, Left Arrow, Up Arrow, Right Arrow, Down Arrow, End, Home.
+
+### NO_HAPTIC_NUMERIC
+With the entry of `#define NO_HAPTIC_NUMERIC` in config.h, none of the following keys between 0 and 9 (KC_1 ... KC_0) will trigger a feedback. \ No newline at end of file
diff --git a/docs/feature_key_overrides.md b/docs/feature_key_overrides.md
new file mode 100644
index 0000000000..861c4bef5d
--- /dev/null
+++ b/docs/feature_key_overrides.md
@@ -0,0 +1,229 @@
+# Key Overrides
+
+Key overrides allow you to override modifier-key combinations to send a different modifier-key combination or perform completely custom actions. Don't want `shift` + `1` to type `!` on your computer? Use a key override to make your keyboard type something different when you press `shift` + `1`. The general behavior is like this: If `modifiers w` + `key x` are pressed, replace these keys with `modifiers y` + `key z` in the keyboard report.
+
+You can use key overrides in a similar way to momentary layer/fn keys to activate custom keycodes/shortcuts, with a number of benefits: You completely keep the original use of the modifier keys, while being able to save space by removing fn keys from your keyboard. You can also easily configure _combinations of modifiers_ to trigger different actions than individual modifiers, and much more. The possibilities are quite vast and this documentation contains a few examples for inspiration throughout.
+
+##### A few more examples to get started: You could use key overrides to...
+- Send `brightness up/down` when pressing `ctrl` + `volume up/down`.
+- Send `delete` when pressing `shift` + `backspace`.
+- Create custom shortcuts or change existing ones: E.g. Send `ctrl`+`shift`+`z` when `ctrl`+`y` is pressed.
+- Run custom code when `ctrl` + `alt` + `esc` is pressed.
+
+## Setup
+
+To enable this feature, you need to add `KEY_OVERRIDE_ENABLE = yes` to your `rules.mk`.
+
+Then, in your `keymap.c` file, you'll need to define the array `key_overrides`, which defines all key overrides to be used. Each override is a value of type `key_override_t`. The array `key_overrides` is `NULL`-terminated and contains pointers to `key_override_t` values (`const key_override_t **`).
+
+## Creating Key Overrides
+
+The `key_override_t` struct has many options that allow you to precisely tune your overrides. The full reference is shown below. Instead of manually creating a `key_override_t` value, it is recommended to use these dedicated initializers:
+
+#### `ko_make_basic(modifiers, key, replacement)`
+Returns a `key_override_t`, which sends `replacement` (can be a key-modifer combination), when `key` and `modifiers` are all pressed down. This override still activates if any additional modifiers not specified in `modifiers` are also pressed down. See `ko_make_with_layers_and_negmods` to customize this behavior.
+
+#### `ko_make_with_layers(modifiers, key, replacement, layers)`
+Additionally takes a bitmask `layers` that defines on which layers the override is used.
+
+#### `ko_make_with_layers_and_negmods(modifiers, key, replacement, layers, negative_mods)`
+Additionally takes a bitmask `negative_mods` that defines which modifiers may not be pressed for this override to activate.
+
+#### `ko_make_with_layers_negmods_and_options(modifiers, key, replacement, layers, negative_mods, options)`
+Additionally takes a bitmask `options` that specifies additional options. See `ko_option_t` for available options.
+
+For more customization possibilities, you may directly create a `key_override_t`, which allows you to customize even more behavior. Read further below for details and examples.
+
+## Simple Example
+
+This shows how the mentioned example of sending `delete` when `shift` + `backspace` are pressed is realized:
+
+```c
+const key_override_t delete_key_override = ko_make_basic(MOD_MASK_SHIFT, KC_BSPACE, KC_DELETE);
+
+// This globally defines all key overrides to be used
+const key_override_t **key_overrides = (const key_override_t *[]){
+ &delete_key_override,
+ NULL // Null terminate the array of overrides!
+};
+```
+
+## Intermediate Difficulty Examples
+
+### Media Controls & Screen Brightness
+
+In this example a single key is configured to control media, volume and screen brightness by using key overrides.
+
+- The key is set to send `play/pause` in the keymap.
+
+The following key overrides will be configured:
+
+- `Ctrl` + `play/pause` will send `next track`.
+- `Ctrl` + `Shift` + `play/pause` will send `previous track`.
+- `Alt` + `play/pause` will send `volume up`.
+- `Alt` + `Shift` + `play/pause` will send `volume down`.
+- `Ctrl` + `Alt` + `play/pause` will send `brightness up`.
+- `Ctrl` + `Alt` + `Shift` + `play/pause` will send `brightness down`.
+
+
+```c
+const key_override_t next_track_override =
+ ko_make_with_layers_negmods_and_options(
+ MOD_MASK_CTRL, // Trigger modifiers: ctrl
+ KC_MPLY, // Trigger key: play/pause
+ KC_MNXT, // Replacement key
+ ~0, // Activate on all layers
+ MOD_MASK_SA, // Do not activate when shift or alt are pressed
+ ko_option_no_reregister_trigger); // Specifies that the play key is not registered again after lifting ctrl
+
+const key_override_t prev_track_override = ko_make_with_layers_negmods_and_options(MOD_MASK_CS, KC_MPLY,
+ KC_MPRV, ~0, MOD_MASK_ALT, ko_option_no_reregister_trigger);
+
+const key_override_t vol_up_override = ko_make_with_layers_negmods_and_options(MOD_MASK_ALT, KC_MPLY,
+ KC_VOLU, ~0, MOD_MASK_CS, ko_option_no_reregister_trigger);
+
+const key_override_t vol_down_override = ko_make_with_layers_negmods_and_options(MOD_MASK_SA, KC_MPLY,
+ KC_VOLD, ~0, MOD_MASK_CTRL, ko_option_no_reregister_trigger);
+
+const key_override_t brightness_up_override = ko_make_with_layers_negmods_and_options(MOD_MASK_CA, KC_MPLY,
+ KC_BRIU, ~0, MOD_MASK_SHIFT, ko_option_no_reregister_trigger);
+
+const key_override_t brightness_down_override = ko_make_basic(MOD_MASK_CSA, KC_MPLY, KC_BRID);
+
+// This globally defines all key overrides to be used
+const key_override_t **key_overrides = (const key_override_t *[]){
+ &next_track_override,
+ &prev_track_override,
+ &vol_up_override,
+ &vol_down_override,
+ &brightness_up_override,
+ &brightness_down_override,
+ NULL
+};
+```
+
+### Flexible macOS-friendly Grave Escape
+The [Grave Escape feature](https://docs.qmk.fm/using-qmk/advanced-keycodes/feature_grave_esc) is limited in its configurability and has [bugs when used on macOS](https://docs.qmk.fm/using-qmk/advanced-keycodes/feature_grave_esc#caveats). Key overrides can be used to achieve a similar functionality as Grave Escape, but with more customization and without bugs on macOS.
+
+```c
+// Shift + esc = ~
+const key_override_t tilde_esc_override = ko_make_basic(MOD_MASK_SHIFT, KC_ESC, S(KC_GRAVE));
+
+// GUI + esc = `
+const key_override_t grave_esc_override = ko_make_basic(MOD_MASK_GUI, KC_ESC, KC_GRAVE);
+
+const key_override_t **key_overrides = (const key_override_t *[]){
+ &tilde_esc_override,
+ &grave_esc_override,
+ NULL
+};
+```
+
+In addition to not encountering unexpected bugs on macOS, you can also change the behavior as you wish. Instead setting `GUI` + `ESC` = `` ` `` you may change it to an arbitrary other modifier, for example `Ctrl` + `ESC` = `` ` ``.
+
+## Advanced Examples
+### Modifiers as Layer Keys
+
+Do you really need a dedicated key to toggle your fn layer? With key overrides, perhaps not. This example shows how you can configure to use `rGUI` + `rAlt` (right GUI and right alt) to access a momentary layer like an fn layer. With this you completely eliminate the need to use a dedicated layer key. Of course the choice of modifier keys can be changed as needed, `rGUI` + `rAlt` is just an example here.
+
+```c
+// This is called when the override activates and deactivates. Enable the fn layer on activation and disable on deactivation
+bool momentary_layer(bool key_down, void *layer) {
+ if (key_down) {
+ layer_on((uint8_t)(uintptr_t)layer);
+ } else {
+ layer_off((uint8_t)(uintptr_t)layer);
+ }
+
+ return false;
+}
+
+const key_override_t fn_override = {.trigger_mods = MOD_BIT(KC_RGUI) | MOD_BIT(KC_RCTL), //
+ .layers = ~(1 << LAYER_FN), //
+ .suppressed_mods = MOD_BIT(KC_RGUI) | MOD_BIT(KC_RCTL), //
+ .options = ko_option_no_unregister_on_other_key_down, //
+ .negative_mod_mask = (uint8_t) ~(MOD_BIT(KC_RGUI) | MOD_BIT(KC_RCTL)), //
+ .custom_action = momentary_layer, //
+ .context = (void *)LAYER_FN, //
+ .trigger = KC_NO, //
+ .replacement = KC_NO, //
+ .enabled = NULL};
+```
+
+## Keycodes
+
+You can enable, disable and toggle all key overrides on the fly.
+
+|Keycode |Description |Function Equivalent|
+|----------|---------------------------------|--------|
+|`KEY_OVERRIDE_ON` |Turns on Key Override feature | `key_override_on(void)`|
+|`KEY_OVERRIDE_OFF` |Turns off Key Override feature |`key_override_off(void)`|
+|`KEY_OVERRIDE_TOGGLE` |Toggles Key Override feature on and off |`key_override_toggle(void)`|
+
+## Reference for `key_override_t`
+
+Advanced users may need more customization than what is offered by the simple `ko_make` initializers. For this, directly create a `key_override_t` value and set all members. Below is a reference for all members of `key_override_t`.
+
+| Member | Description |
+|--------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `uint16_t trigger` | The non-modifier keycode that triggers the override. This keycode, and the necessary modifiers (`trigger_mods`) must be pressed to activate this override. Set this to the keycode of the key that should activate the override. Set to `KC_NO` to require only the necessary modifiers to be pressed and no non-modifier. |
+| `uint8_t trigger_mods` | Which mods need to be down for activation. If both sides of a modifier are set (e.g. left ctrl and right ctrl) then only one is required to be pressed (e.g. left ctrl suffices). Use the `MOD_MASK_XXX` and `MOD_BIT()` macros for this. |
+| `layer_state_t layers` | This is a BITMASK (!), defining which layers this override applies to. To use this override on layer i set the ith bit `(1 << i)`. |
+| `uint8_t negative_mod_mask` | Which modifiers cannot be down. It must hold that `(active_modifiers & negative_mod_mask) == 0`, otherwise the key override will not be activated. An active override will be deactivated once this is no longer true. |
+| `uint8_t suppressed_mods` | Modifiers to 'suppress' while the override is active. To suppress a modifier means that even though the modifier key is held down, the host OS sees the modifier as not pressed. Can be used to suppress the trigger modifiers, as a trivial example. |
+| `uint16_t replacement` | The complex keycode to send as replacement when this override is triggered. This can be a simple keycode, a key-modifier combination (e.g. `C(KC_A)`), or `KC_NO` (to register no replacement keycode). Use in combination with suppressed_mods to get the correct modifiers to be sent. |
+| `ko_option_t options` | Options controlling the behavior of the override, such as what actions are allowed to activate the override. |
+| `bool (*custom_action)(bool activated, void *context)` | If not NULL, this function will be called right before the replacement key is registered, along with the provided context and a flag indicating whether the override was activated or deactivated. This function allows you to run some custom actions for specific key overrides. If you return `false`, the replacement key is not registered/unregistered as it would normally. Return `true` to register and unregister the override normally. |
+| `void *context` | A context that will be passed to the custom action function. |
+| `bool *enabled` | If this points to false this override will not be used. Set to NULL to always have this override enabled. |
+
+### Reference for `ko_option_t`
+
+Bitfield with various options controlling the behavior of a key override.
+
+| Value | Description |
+|------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `ko_option_activation_trigger_down` | Allow activating when the trigger key is pressed down. |
+| `ko_option_activation_required_mod_down` | Allow activating when a necessary modifier is pressed down. |
+| `ko_option_activation_negative_mod_up` | Allow activating when a negative modifier is released. |
+| `ko_option_one_mod` | If set, any of the modifiers in `trigger_mods` will be enough to activate the override (logical OR of modifiers). If not set, all the modifiers in `trigger_mods` have to be pressed (logical AND of modifiers). |
+| `ko_option_no_unregister_on_other_key_down` | If set, the override will not deactivate when another key is pressed down. Use only if you really know you need this. |
+| `ko_option_no_reregister_trigger` | If set, the trigger key will never be registered again after the override is deactivated. |
+| `ko_options_default` | The default options used by the `ko_make_xxx` functions |
+
+## For Advanced Users: Inner Workings
+
+This section explains how a key override works in detail, explaining where each member of `key_override_t` comes into play. Understanding this is essential to be able to take full advantage of all the options offered by key overrides.
+
+#### Activation
+
+When the necessary keys are pressed (`trigger_mods` + `trigger`), the override is 'activated' and the replacement key is registered in the keyboard report (`replacement`), while the `trigger` key is removed from the keyboard report. The trigger modifiers may also be removed from the keyboard report upon activation of an override (`suppressed_mods`). The override will not activate if any of the `negative_modifiers` are pressed.
+
+Overrides can activate in three different cases:
+
+1. The trigger key is pressed down and necessary modifiers are already down.
+2. A necessary modifier is pressed down, while the trigger key and other necessary modifiers are already down.
+3. A negative modifier is released, while all necessary modifiers and the trigger key are already down.
+
+Use the `option` member to customize which of these events are allowed to activate your overrides (default: all three).
+
+In any case, a key override can only activate if the `trigger` key is the _last_ non-modifier key that was pressed down. This emulates the behavior of how standard OSes (macOS, Windows, Linux) handle normal key input (to understand: Hold down `a`, then also hold down `b`, then hold down `shift`; `B` will be typed but not `A`).
+
+#### Deactivation
+
+An override is 'deactivated' when one of the trigger keys (`trigger_mods`, `trigger`) is lifted, another non-modifier key is pressed down, or one of the `negative_modifiers` is pressed down. When an override deactivates, the `replacement` key is removed from the keyboard report, while the `suppressed_mods` that are still held down are re-added to the keyboard report. By default, the `trigger` key is re-added to the keyboard report if it is still held down and no other non-modifier key has been pressed since. This again emulates the behavior of how standard OSes handle normal key input (To understand: hold down `a`, then also hold down `b`, then also `shift`, then release `b`; `A` will not be typed even though you are holding the `a` and `shift` keys). Use the `option` field `ko_option_no_reregister_trigger` to prevent re-registering the trigger key in all cases.
+
+#### Key Repeat Delay
+
+A third way in which standard OS-handling of modifier-key input is emulated in key overrides is with a ['key repeat delay'](https://www.dummies.com/computers/pcs/set-your-keyboards-repeat-delay-and-repeat-rate/). To explain what this is, let's look at how normal keyboard input is handled by mainstream OSes again: If you hold down `a`, followed by `shift`, you will see the letter `a` is first typed, then for a short moment nothing is typed and then repeating `A`s are typed. Take note that, although shift is pressed down just after `a` is pressed, it takes a moment until `A` is typed. This is caused by the aforementioned key repeat delay, and it is a feature that prevents unwanted repeated characters from being typed.
+
+This applies equally to releasing a modifier: When you hold `shift`, then press `a`, the letter `A` is typed. Now if you release `shift` first, followed by `a` shortly after, you will not see the letter `a` being typed, even though for a short moment of time you were just holding down the key `a`. This is because no modified characters are typed until the key repeat delay has passed.
+
+ This exact behavior is implemented in key overrides as well: If a key override for `shift` + `a` = `b` exists, and `a` is pressed and held, followed by `shift`, you will not immediately see the letter `b` being typed. Instead, this event is deferred for a short moment, until the key repeat delay has passed, measured from the moment when the trigger key (`a`) was pressed down.
+
+The duration of the key repeat delay is controlled with the `KEY_OVERRIDE_REPEAT_DELAY` macro. Define this value in your `config.h` file to change it. It is 500ms by default.
+
+
+## Difference to Combos
+
+Note that key overrides are very different from [combos](https://docs.qmk.fm/#/feature_combo). Combos require that you press down several keys almost _at the same time_ and can work with any combination of non-modifier keys. Key overrides work like keyboard shortcuts (e.g. `ctrl` + `z`): They take combinations of _multiple_ modifiers and _one_ non-modifier key to then perform some custom action. Key overrides are implemented with much care to behave just like normal keyboard shortcuts would in regards to the order of pressed keys, timing, and interacton with other pressed keys. There are a number of optional settings that can be used to really fine-tune the behavior of each key override as well. Using key overrides also does not delay key input for regular key presses, which inherently happens in combos and may be undesirable.
diff --git a/docs/feature_oled_driver.md b/docs/feature_oled_driver.md
index f3b659b1bc..c90aabb9c6 100644
--- a/docs/feature_oled_driver.md
+++ b/docs/feature_oled_driver.md
@@ -346,6 +346,10 @@ bool oled_scroll_left(void);
// Returns true if the screen was not scrolling or stops scrolling
bool oled_scroll_off(void);
+// Inverts the display
+// Returns true if the screen was or is inverted
+bool oled_invert(bool invert);
+
// Returns the maximum number of characters that will fit on a line
uint8_t oled_max_chars(void);
diff --git a/docs/feature_rgb_matrix.md b/docs/feature_rgb_matrix.md
index 08d5c9c4c6..f3e25f2f13 100644
--- a/docs/feature_rgb_matrix.md
+++ b/docs/feature_rgb_matrix.md
@@ -145,9 +145,22 @@ There is basic support for addressable RGB matrix lighting with the I2C IS31FL37
RGB_MATRIX_ENABLE = yes
RGB_MATRIX_DRIVER = IS31FL3737
```
+You can use between 1 and 2 IS31FL3737 IC's. Do not specify `DRIVER_ADDR_2` define for second IC if not present on your keyboard.
Configure the hardware via your `config.h`:
+| Variable | Description | Default |
+|----------|-------------|---------|
+| `ISSI_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 |
+| `ISSI_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 |
+| `DRIVER_COUNT` | (Required) How many RGB driver IC's are present | |
+| `DRIVER_LED_TOTAL` | (Required) How many RGB lights are present across all drivers | |
+| `DRIVER_ADDR_1` | (Required) Address for the first RGB driver | |
+| `DRIVER_ADDR_2` | (Optional) Address for the second RGB driver | |
+
+
+Here is an example using 2 drivers.
+
```c
// This is a 7-bit address, that gets left-shifted and bit 0
// set to 0 for write, 1 for read (as per I2C protocol)
@@ -159,19 +172,21 @@ Configure the hardware via your `config.h`:
// ADDR represents A3:A0 of the 7-bit address.
// The result is: 0b101(ADDR)
#define DRIVER_ADDR_1 0b1010000
-#define DRIVER_ADDR_2 0b1010000 // this is here for compliancy reasons.
+#define DRIVER_ADDR_2 0b1010001
#define DRIVER_COUNT 2
-#define DRIVER_1_LED_TOTAL 64
-#define DRIVER_LED_TOTAL DRIVER_1_LED_TOTAL
+#define DRIVER_1_LED_TOTAL 30
+#define DRIVER_2_LED_TOTAL 36
+#define DRIVER_LED_TOTAL (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
```
+!> Note the parentheses, this is so when `DRIVER_LED_TOTAL` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`.
-Currently only a single drivers is supported, but it would be trivial to support all 4 combinations. For now define `DRIVER_ADDR_2` as `DRIVER_ADDR_1`
+Currently only 2 drivers are supported, but it would be trivial to support all 4 combinations.
Define these arrays listing all the LEDs in your `<keyboard>.c`:
```c
-const is31_led g_is31_leds[DRIVER_LED_TOTAL] = {
+const is31_led PROGMEM g_is31_leds[DRIVER_LED_TOTAL] = {
/* Refer to IS31 manual for these locations
* driver
* | R location
@@ -183,7 +198,7 @@ const is31_led g_is31_leds[DRIVER_LED_TOTAL] = {
}
```
-Where `X_Y` is the location of the LED in the matrix defined by [the datasheet](https://www.issi.com/WW/pdf/31FL3737.pdf) and the header file `drivers/issi/is31fl3737.h`. The `driver` is the index of the driver you defined in your `config.h` (Only `0` right now).
+Where `X_Y` is the location of the LED in the matrix defined by [the datasheet](https://www.issi.com/WW/pdf/31FL3737.pdf) and the header file `drivers/issi/is31fl3737.h`. The `driver` is the index of the driver you defined in your `config.h` (Only `0`, `1` for now).
---
@@ -228,6 +243,75 @@ Configure the hardware via your `config.h`:
```
---
+### AW20216 :id=aw20216
+There is basic support for addressable RGB matrix lighting with the SPI AW20216 RGB controller. To enable it, add this to your `rules.mk`:
+
+```makefile
+RGB_MATRIX_ENABLE = yes
+RGB_MATRIX_DRIVER = AW20216
+```
+
+You can use up to 2 AW20216 IC's. Do not specify `DRIVER_<N>_xxx` defines for IC's that are not present on your keyboard. You can define the following items in `config.h`:
+
+| Variable | Description | Default |
+|----------|-------------|---------|
+| `DRIVER_1_CS` | (Required) MCU pin connected to first RGB driver chip select line | B13 |
+| `DRIVER_2_CS` | (Optional) MCU pin connected to second RGB driver chip select line | |
+| `DRIVER_1_EN` | (Required) MCU pin connected to first RGB driver hardware enable line | C13 |
+| `DRIVER_2_EN` | (Optional) MCU pin connected to second RGB driver hardware enable line | |
+| `DRIVER_1_LED_TOTAL` | (Required) How many RGB lights are connected to first RGB driver | |
+| `DRIVER_2_LED_TOTAL` | (Optional) How many RGB lights are connected to second RGB driver | |
+| `DRIVER_COUNT` | (Required) How many RGB driver IC's are present | |
+| `DRIVER_LED_TOTAL` | (Required) How many RGB lights are present across all drivers | |
+| `AW_SCALING_MAX` | (Optional) LED current scaling value (0-255, higher values mean LED is brighter at full PWM) | 150 |
+| `AW_GLOBAL_CURRENT_MAX` | (Optional) Driver global current limit (0-255, higher values means the driver may consume more power) | 150 |
+| `AW_SPI_DIVISOR` | (Optional) Clock divisor for SPI communication (powers of 2, smaller numbers means faster communication, should not be less than 4) | 4 |
+
+Here is an example using 2 drivers.
+
+```c
+#define DRIVER_1_CS B13
+#define DRIVER_2_CS B14
+// Hardware enable lines may be connected to the same pin
+#define DRIVER_1_EN C13
+#define DRIVER_2_EN C13
+
+#define DRIVER_COUNT 2
+#define DRIVER_1_LED_TOTAL 66
+#define DRIVER_2_LED_TOTAL 32
+#define DRIVER_LED_TOTAL (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
+```
+
+!> Note the parentheses, this is so when `DRIVER_LED_TOTAL` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`.
+
+Define these arrays listing all the LEDs in your `<keyboard>.c`:
+
+```c
+const aw_led g_aw_leds[DRIVER_LED_TOTAL] = {
+/* Each AW20216 channel is controlled by a register at some offset between 0x00
+ * and 0xD7 inclusive.
+ * See drivers/awinic/aw20216.h for the mapping between register offsets and
+ * driver pin locations.
+ * driver
+ * | R location
+ * | | G location
+ * | | | B location
+ * | | | | */
+ { 0, CS1_SW1, CS2_SW1, CS3_SW1 },
+ { 0, CS4_SW1, CS5_SW1, CS6_SW1 },
+ { 0, CS7_SW1, CS8_SW1, CS9_SW1 },
+ { 0, CS10_SW1, CS11_SW1, CS12_SW1 },
+ { 0, CS13_SW1, CS14_SW1, CS15_SW1 },
+ ...
+ { 1, CS1_SW1, CS2_SW1, CS3_SW1 },
+ { 1, CS13_SW1, CS14_SW1, CS15_SW1 },
+ { 1, CS16_SW1, CS17_SW1, CS18_SW1 },
+ { 1, CS4_SW2, CS5_SW2, CS6_SW2 },
+ ...
+};
+```
+
+---
## Common Configuration :id=common-configuration
@@ -485,28 +569,29 @@ For inspiration and examples, check out the built-in effects under `quantum/rgb_
These are shorthands to popular colors. The `RGB` ones can be passed to the `setrgb` functions, while the `HSV` ones to the `sethsv` functions.
-|RGB |HSV |
-|-------------------|-------------------|
-|`RGB_WHITE` |`HSV_WHITE` |
-|`RGB_RED` |`HSV_RED` |
-|`RGB_CORAL` |`HSV_CORAL` |
-|`RGB_ORANGE` |`HSV_ORANGE` |
-|`RGB_GOLDENROD` |`HSV_GOLDENROD` |
-|`RGB_GOLD` |`HSV_GOLD` |
-|`RGB_YELLOW` |`HSV_YELLOW` |
-|`RGB_CHARTREUSE` |`HSV_CHARTREUSE` |
-|`RGB_GREEN` |`HSV_GREEN` |
-|`RGB_SPRINGGREEN` |`HSV_SPRINGGREEN` |
-|`RGB_TURQUOISE` |`HSV_TURQUOISE` |
-|`RGB_TEAL` |`HSV_TEAL` |
-|`RGB_CYAN` |`HSV_CYAN` |
-|`RGB_AZURE` |`HSV_AZURE` |
-|`RGB_BLUE` |`HSV_BLUE` |
-|`RGB_PURPLE` |`HSV_PURPLE` |
-|`RGB_MAGENTA` |`HSV_MAGENTA` |
-|`RGB_PINK` |`HSV_PINK` |
-
-These are defined in [`rgblight_list.h`](https://github.com/qmk/qmk_firmware/blob/master/quantum/rgblight_list.h). Feel free to add to this list!
+|RGB |HSV |
+|---------------------|---------------------|
+|`RGB_AZURE` |`HSV_AZURE` |
+|`RGB_BLACK`/`RGB_OFF`|`HSV_BLACK`/`HSV_OFF`|
+|`RGB_BLUE` |`HSV_BLUE` |
+|`RGB_CHARTREUSE` |`HSV_CHARTREUSE` |
+|`RGB_CORAL` |`HSV_CORAL` |
+|`RGB_CYAN` |`HSV_CYAN` |
+|`RGB_GOLD` |`HSV_GOLD` |
+|`RGB_GOLDENROD` |`HSV_GOLDENROD` |
+|`RGB_GREEN` |`HSV_GREEN` |
+|`RGB_MAGENTA` |`HSV_MAGENTA` |
+|`RGB_ORANGE` |`HSV_ORANGE` |
+|`RGB_PINK` |`HSV_PINK` |
+|`RGB_PURPLE` |`HSV_PURPLE` |
+|`RGB_RED` |`HSV_RED` |
+|`RGB_SPRINGGREEN` |`HSV_SPRINGGREEN` |
+|`RGB_TEAL` |`HSV_TEAL` |
+|`RGB_TURQUOISE` |`HSV_TURQUOISE` |
+|`RGB_WHITE` |`HSV_WHITE` |
+|`RGB_YELLOW` |`HSV_YELLOW` |
+
+These are defined in [`color.h`](https://github.com/qmk/qmk_firmware/blob/master/quantum/color.h). Feel free to add to this list!
## Additional `config.h` Options :id=additional-configh-options
diff --git a/docs/feature_rgblight.md b/docs/feature_rgblight.md
index 994a014a28..d323dee4f7 100644
--- a/docs/feature_rgblight.md
+++ b/docs/feature_rgblight.md
@@ -119,7 +119,7 @@ if `RGBLIGHT_EFFECT_xxxx` or `RGBLIGHT_ANIMATIONS` is defined, you also have a n
Check out [this video](https://youtube.com/watch?v=VKrpPAHlisY) for a demonstration.
-Note: For versions older than 0.6.117, The mode numbers were written directly. In `quantum/rgblight.h` there is a contrast table between the old mode number and the current symbol.
+Note: For versions older than 0.6.117, The mode numbers were written directly. In `quantum/rgblight/rgblight.h` there is a contrast table between the old mode number and the current symbol.
### Effect and Animation Toggles
@@ -328,7 +328,7 @@ Normally lighting layers are not shown when RGB Lighting is disabled (e.g. with
## Functions
-If you need to change your RGB lighting in code, for example in a macro to change the color whenever you switch layers, QMK provides a set of functions to assist you. See [`rgblight.h`](https://github.com/qmk/qmk_firmware/blob/master/quantum/rgblight.h) for the full list, but the most commonly used functions include:
+If you need to change your RGB lighting in code, for example in a macro to change the color whenever you switch layers, QMK provides a set of functions to assist you. See [`rgblight.h`](https://github.com/qmk/qmk_firmware/blob/master/quantum/rgblight/rgblight.h) for the full list, but the most commonly used functions include:
### Utility Functions
|Function |Description |
@@ -449,26 +449,27 @@ rgblight_sethsv_at(HSV_GREEN, 2); // led 2
These are shorthands to popular colors. The `RGB` ones can be passed to the `setrgb` functions, while the `HSV` ones to the `sethsv` functions.
-|RGB |HSV |
-|-------------------|-------------------|
-|`RGB_WHITE` |`HSV_WHITE` |
-|`RGB_RED` |`HSV_RED` |
-|`RGB_CORAL` |`HSV_CORAL` |
-|`RGB_ORANGE` |`HSV_ORANGE` |
-|`RGB_GOLDENROD` |`HSV_GOLDENROD` |
-|`RGB_GOLD` |`HSV_GOLD` |
-|`RGB_YELLOW` |`HSV_YELLOW` |
-|`RGB_CHARTREUSE` |`HSV_CHARTREUSE` |
-|`RGB_GREEN` |`HSV_GREEN` |
-|`RGB_SPRINGGREEN` |`HSV_SPRINGGREEN` |
-|`RGB_TURQUOISE` |`HSV_TURQUOISE` |
-|`RGB_TEAL` |`HSV_TEAL` |
-|`RGB_CYAN` |`HSV_CYAN` |
-|`RGB_AZURE` |`HSV_AZURE` |
-|`RGB_BLUE` |`HSV_BLUE` |
-|`RGB_PURPLE` |`HSV_PURPLE` |
-|`RGB_MAGENTA` |`HSV_MAGENTA` |
-|`RGB_PINK` |`HSV_PINK` |
+|RGB |HSV |
+|---------------------|---------------------|
+|`RGB_AZURE` |`HSV_AZURE` |
+|`RGB_BLACK`/`RGB_OFF`|`HSV_BLACK`/`HSV_OFF`|
+|`RGB_BLUE` |`HSV_BLUE` |
+|`RGB_CHARTREUSE` |`HSV_CHARTREUSE` |
+|`RGB_CORAL` |`HSV_CORAL` |
+|`RGB_CYAN` |`HSV_CYAN` |
+|`RGB_GOLD` |`HSV_GOLD` |
+|`RGB_GOLDENROD` |`HSV_GOLDENROD` |
+|`RGB_GREEN` |`HSV_GREEN` |
+|`RGB_MAGENTA` |`HSV_MAGENTA` |
+|`RGB_ORANGE` |`HSV_ORANGE` |
+|`RGB_PINK` |`HSV_PINK` |
+|`RGB_PURPLE` |`HSV_PURPLE` |
+|`RGB_RED` |`HSV_RED` |
+|`RGB_SPRINGGREEN` |`HSV_SPRINGGREEN` |
+|`RGB_TEAL` |`HSV_TEAL` |
+|`RGB_TURQUOISE` |`HSV_TURQUOISE` |
+|`RGB_WHITE` |`HSV_WHITE` |
+|`RGB_YELLOW` |`HSV_YELLOW` |
```c
rgblight_setrgb(RGB_ORANGE);
@@ -477,7 +478,7 @@ rgblight_setrgb_at(RGB_GOLD, 3);
rgblight_sethsv_range(HSV_WHITE, 0, 6);
```
-These are defined in [`rgblight_list.h`](https://github.com/qmk/qmk_firmware/blob/master/quantum/rgblight_list.h). Feel free to add to this list!
+These are defined in [`color.h`](https://github.com/qmk/qmk_firmware/blob/master/quantum/color.h). Feel free to add to this list!
## Changing the order of the LEDs
diff --git a/docs/feature_split_keyboard.md b/docs/feature_split_keyboard.md
index 4ebf585f5c..428d581cab 100644
--- a/docs/feature_split_keyboard.md
+++ b/docs/feature_split_keyboard.md
@@ -8,8 +8,7 @@ QMK Firmware has a generic implementation that is usable by any board, as well a
For this, we will mostly be talking about the generic implementation used by the Let's Split and other keyboards.
-!> ARM is not yet fully supported for Split Keyboards and has many limitations. Progress is being made, but we have not yet reached 100% feature parity.
-
+!> ARM split supports most QMK subsystems when using the 'serial' and 'serial_usart' drivers. I2C slave is currently unsupported.
## Compatibility Overview
@@ -90,7 +89,13 @@ You can configure the firmware to read a pin on the controller to determine hand
#define SPLIT_HAND_PIN B7
```
-This will read the specified pin. If it's high, then the controller assumes it is the left hand, and if it's low, it's assumed to be the right side.
+This will read the specified pin. By default, if it's high, then the controller assumes it is the left hand, and if it's low, it's assumed to be the right side.
+
+This behaviour can be flipped by adding this to you `config.h` file:
+
+```c
+#define SPLIT_HAND_PIN_LOW_IS_LEFT
+```
#### Handedness by Matrix Pin
@@ -169,7 +174,7 @@ Because not every split keyboard is identical, there are a number of additional
#define USE_I2C
```
-This enables I<sup>2</sup>C support for split keyboards. This isn't strictly for communication, but can be used for OLED or other I<sup>2</sup>C-based devices.
+This configures the use of I<sup>2</sup>C support for split keyboard transport (AVR only).
```c
#define SOFT_SERIAL_PIN D0
@@ -193,20 +198,115 @@ If you're having issues with serial communication, you can change this value, as
* **`5`**: about 20kbps
```c
-#define SPLIT_MODS_ENABLE
+#define FORCED_SYNC_THROTTLE_MS 100
```
-This enables transmitting modifier state (normal, weak and oneshot) to the non
-primary side of the split keyboard. This adds a few bytes of data to the split
-communication protocol and may impact the matrix scan speed when enabled.
-The purpose of this feature is to support cosmetic use of modifer state (e.g.
-displaying status on an OLED screen).
+This sets the maximum number of milliseconds before forcing a synchronization of data from master to slave. Under normal circumstances this sync occurs whenever the data _changes_, for safety a data transfer occurs after this number of milliseconds if no change has been detected since the last sync.
```c
#define SPLIT_TRANSPORT_MIRROR
```
-This mirrors the master side matrix to the slave side for features that react or require knowledge of master side key presses on the slave side. This adds a few bytes of data to the split communication protocol and may impact the matrix scan speed when enabled. The purpose of this feature is to support cosmetic use of key events (e.g. RGB reacting to Keypresses).
+This mirrors the master side matrix to the slave side for features that react or require knowledge of master side key presses on the slave side. The purpose of this feature is to support cosmetic use of key events (e.g. RGB reacting to keypresses). This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled.
+
+```c
+#define SPLIT_LAYER_STATE_ENABLE
+```
+
+This enables syncing of the layer state between both halves of the split keyboard. The main purpose of this feature is to enable support for use of things like OLED display of the currently active layer. This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled.
+
+```c
+#define SPLIT_LED_STATE_ENABLE
+```
+
+This enables syncing of the Host LED status (caps lock, num lock, etc) between both halves of the split keyboard. The main purpose of this feature is to enable support for use of things like OLED display of the Host LED status. This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled.
+
+```c
+#define SPLIT_MODS_ENABLE
+```
+
+This enables transmitting modifier state (normal, weak and oneshot) to the non primary side of the split keyboard. The purpose of this feature is to support cosmetic use of modifer state (e.g. displaying status on an OLED screen). This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled.
+
+```c
+#define SPLIT_WPM_ENABLE
+```
+
+This enables transmitting the current WPM to the slave side of the split keyboard. The purpose of this feature is to support cosmetic use of WPM (e.g. displaying the current value on an OLED screen). This adds overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled.
+
+### Custom data sync between sides :id=custom-data-sync
+
+QMK's split transport allows for arbitrary data transactions at both the keyboard and user levels. This is modelled on a remote procedure call, with the master invoking a function on the slave side, with the ability to send data from master to slave, process it slave side, and send data back from slave to master.
+
+To leverage this, a keyboard or user/keymap can define a comma-separated list of _transaction IDs_:
+
+```c
+// for keyboard-level data sync:
+#define SPLIT_TRANSACTION_IDS_KB KEYBOARD_SYNC_A, KEYBOARD_SYNC_B
+// or, for user:
+#define SPLIT_TRANSACTION_IDS_USER USER_SYNC_A, USER_SYNC_B, USER_SYNC_C
+```
+
+These _transaction IDs_ then need a slave-side handler function to be registered with the split transport, for example:
+
+```c
+typedef struct _master_to_slave_t {
+ int m2s_data;
+} master_to_slave_t;
+
+typedef struct _slave_to_master_t {
+ int s2m_data;
+} slave_to_master_t;
+
+void user_sync_a_slave_handler(uint8_t in_buflen, const void* in_data, uint8_t out_buflen, void* out_data) {
+ const master_to_slave_t *m2s = (const master_to_slave_t*)in_data;
+ slave_to_master_t *s2m = (slave_to_master_t*)out_data;
+ s2m->s2m_data = m2s->m2s_data + 5; // whatever comes in, add 5 so it can be sent back
+}
+
+void keyboard_post_init_user(void) {
+ transaction_register_rpc(USER_SYNC_A, user_sync_a_slave_handler);
+}
+```
+
+The master side can then invoke the slave-side handler - for normal keyboard functionality to be minimally affected, any keyboard- or user-level code attempting to sync data should be throttled:
+
+```c
+void housekeeping_task_user(void) {
+ if (is_keyboard_master()) {
+ // Interact with slave every 500ms
+ static uint32_t last_sync = 0;
+ if (timer_elapsed32(last_sync) > 500) {
+ master_to_slave_t m2s = {6};
+ slave_to_master_t s2m = {0};
+ if(transaction_rpc_exec(USER_SYNC_A, sizeof(m2s), &m2s, sizeof(s2m), &s2m)) {
+ last_sync = timer_read32();
+ dprintf("Slave value: %d\n", s2m.s2m_data); // this will now be 11, as the slave adds 5
+ } else {
+ dprint("Slave sync failed!\n");
+ }
+ }
+ }
+}
+```
+
+!> It is recommended that any data sync between halves happens during the master side's _housekeeping task_. This ensures timely retries should failures occur.
+
+If only one-way data transfer is needed, helper methods are provided:
+
+```c
+bool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
+bool transaction_rpc_send(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer);
+bool transaction_rpc_recv(int8_t transaction_id, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);
+```
+
+By default, the inbound and outbound data is limited to a maximum of 32 bytes each. The sizes can be altered if required:
+
+```c
+// Master to slave:
+#define RPC_M2S_BUFFER_SIZE 48
+// Slave to master:
+#define RPC_S2M_BUFFER_SIZE 48
+```
### Hardware Configuration Options
diff --git a/docs/feature_st7565.md b/docs/feature_st7565.md
new file mode 100644
index 0000000000..de3e44d8e9
--- /dev/null
+++ b/docs/feature_st7565.md
@@ -0,0 +1,274 @@
+# ST7565 LCD Driver
+
+## Supported Hardware
+
+LCD modules using ST7565 driver IC, communicating over SPI.
+
+|Module |IC |Size |Notes |
+|------------------------------|-------|------|----------------------------------------------------------|
+|Newhaven Display NHD-C12832A1Z|ST7565R|128x32|Used by Ergodox Infinity; primary consumer of this feature|
+|Zolentech ZLE12864B |ST7565P|128x64|Requires contrast adjustment |
+
+## Usage
+
+To enable the feature, there are three steps. First, when compiling your keyboard, you'll need to add the following to your `rules.mk`:
+
+```make
+ST7565_ENABLE = yes
+```
+
+Then in your `keymap.c` file, implement the ST7565 task call. This example assumes your keymap has three layers named `_QWERTY`, `_FN` and `_ADJ`:
+
+```c
+#ifdef ST7565_ENABLE
+void st7565_task_user(void) {
+ // Host Keyboard Layer Status
+ st7565_write_P(PSTR("Layer: "), false);
+
+ switch (get_highest_layer(layer_state)) {
+ case _QWERTY:
+ st7565_write_P(PSTR("Default\n"), false);
+ break;
+ case _FN:
+ st7565_write_P(PSTR("FN\n"), false);
+ break;
+ case _ADJ:
+ st7565_write_P(PSTR("ADJ\n"), false);
+ break;
+ default:
+ // Or use the write_ln shortcut over adding '\n' to the end of your string
+ st7565_write_ln_P(PSTR("Undefined"), false);
+ }
+
+ // Host Keyboard LED Status
+ led_t led_state = host_keyboard_led_state();
+ st7565_write_P(led_state.num_lock ? PSTR("NUM ") : PSTR(" "), false);
+ st7565_write_P(led_state.caps_lock ? PSTR("CAP ") : PSTR(" "), false);
+ st7565_write_P(led_state.scroll_lock ? PSTR("SCR ") : PSTR(" "), false);
+}
+#endif
+```
+
+## Logo Example
+
+In the default font, certain ranges of characters are reserved for a QMK logo. To render this logo to the screen, use the following code example:
+
+```c
+static void render_logo(void) {
+ static const char PROGMEM qmk_logo[] = {
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94,
+ 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4,
+ 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0x00
+ };
+
+ st7565_write_P(qmk_logo, false);
+}
+```
+
+## Buffer Read Example
+For some purposes, you may need to read the current state of the display buffer. The `st7565_read_raw` function can be used to safely read bytes from the buffer.
+
+In this example, calling `fade_display` in the `st7565_task_user` function will slowly fade away whatever is on the screen by turning random pixels off over time.
+```c
+//Setup some mask which can be or'd with bytes to turn off pixels
+const uint8_t single_bit_masks[8] = {127, 191, 223, 239, 247, 251, 253, 254};
+
+static void fade_display(void) {
+ //Define the reader structure
+ display_buffer_reader_t reader;
+ uint8_t buff_char;
+ if (random() % 30 == 0) {
+ srand(timer_read());
+ // Fetch a pointer for the buffer byte at index 0. The return structure
+ // will have the pointer and the number of bytes remaining from this
+ // index position if we want to perform a sequential read by
+ // incrementing the buffer pointer
+ reader = st7565_read_raw(0);
+ //Loop over the remaining buffer and erase pixels as we go
+ for (uint16_t i = 0; i < reader.remaining_element_count; i++) {
+ //Get the actual byte in the buffer by dereferencing the pointer
+ buff_char = *reader.current_element;
+ if (buff_char != 0) {
+ st7565_write_raw_byte(buff_char & single_bit_masks[rand() % 8], i);
+ }
+ //increment the pointer to fetch a new byte during the next loop
+ reader.current_element++;
+ }
+ }
+}
+```
+
+## Other Examples
+
+In split keyboards, it is very common to have two displays that each render different content and are oriented or flipped differently. You can do this by switching which content to render by using the return value from `is_keyboard_master()` or `is_keyboard_left()` found in `split_util.h`, e.g:
+
+```c
+#ifdef ST7565_ENABLE
+display_rotation_t st7565_init_user(display_rotation_t rotation) {
+ if (!is_keyboard_master()) {
+ return DISPLAY_ROTATION_180; // flips the display 180 degrees if offhand
+ }
+
+ return rotation;
+}
+
+void st7565_task_user(void) {
+ if (is_keyboard_master()) {
+ render_status(); // Renders the current keyboard state (layer, lock, caps, scroll, etc)
+ } else {
+ render_logo(); // Renders a static logo
+ }
+}
+#endif
+```
+
+## Basic Configuration
+
+|Define |Default |Description |
+|------------------------|--------------|-----------------------------------------------------------------------------------------------------|
+|`ST7565_A0_PIN` |*Not defined* |(Required) The GPIO connected to the display's A0 (data/command) pin |
+|`ST7565_RST_PIN` |*Not defined* |(Required) The GPIO connected to the display's reset pin |
+|`ST7565_SS_PIN` |*Not defined* |(Required) The GPIO connected to the display's slave select pin |
+|`ST7565_SPI_CLK_DIVISOR`|`4` |The SPI clock divisor to use |
+|`ST7565_FONT_H` |`"glcdfont.c"`|The font code file to use for custom fonts |
+|`ST7565_FONT_START` |`0` |The starting character index for custom fonts |
+|`ST7565_FONT_END` |`223` |The ending character index for custom fonts |
+|`ST7565_FONT_WIDTH` |`6` |The font width |
+|`ST7565_FONT_HEIGHT` |`8` |The font height (untested) |
+|`ST7565_TIMEOUT` |`60000` |Turns off the screen after 60000ms of keyboard inactivity. Helps reduce burn-in. Set to 0 to disable.|
+|`ST7565_COLUMN_OFFSET` |`0` |Shift output to the right this many pixels. |
+|`ST7565_CONTRAST` |`32` |The default contrast level of the display, from 0 to 255. |
+|`ST7565_UPDATE_INTERVAL`|`0` |Set the time interval for updating the display in ms. This will improve the matrix scan rate. |
+
+## Custom sized displays
+
+The default display size for this feature is 128x32 and all necessary defines are precalculated with that in mind.
+
+|Define |Default |Description |
+|-----------------------|----------|-----------------------------------------------------------------------------------------------------------|
+|`ST7565_DISPLAY_WIDTH` |`128` |The width of the display. |
+|`ST7565_DISPLAY_HEIGHT`|`32` |The height of the display. |
+|`ST7565_MATRIX_SIZE` |`512` |The local buffer size to allocate.<br>`(ST7565_DISPLAY_HEIGHT / 8 * ST7565_DISPLAY_WIDTH)`. |
+|`ST7565_BLOCK_TYPE` |`uint16_t`|The unsigned integer type to use for dirty rendering. |
+|`ST7565_BLOCK_COUNT` |`16` |The number of blocks the display is divided into for dirty rendering.<br>`(sizeof(ST7565_BLOCK_TYPE) * 8)`.|
+|`ST7565_BLOCK_SIZE` |`32` |The size of each block for dirty rendering<br>`(ST7565_MATRIX_SIZE / ST7565_BLOCK_COUNT)`. |
+
+## API
+
+```c
+// Rotation enum values are flags
+typedef enum {
+ DISPLAY_ROTATION_0,
+ DISPLAY_ROTATION_180
+} display_rotation_t;
+
+// Initialize the display, rotating the rendered output based on the define passed in.
+// Returns true if the was initialized successfully
+bool st7565_init(display_rotation_t rotation);
+
+// Called at the start of st7565_init, weak function overridable by the user
+// rotation - the value passed into st7565_init
+// Return new display_rotation_t if you want to override default rotation
+display_rotation_t st7565_init_user(display_rotation_t rotation);
+
+// Clears the display buffer, resets cursor position to 0, and sets the buffer to dirty for rendering
+void st7565_clear(void);
+
+// Renders the dirty chunks of the buffer to display
+void st7565_render(void);
+
+// Moves cursor to character position indicated by column and line, wraps if out of bounds
+// Max column denoted by 'st7565_max_chars()' and max lines by 'st7565_max_lines()' functions
+void st7565_set_cursor(uint8_t col, uint8_t line);
+
+// Advances the cursor to the next page, writing ' ' if true
+// Wraps to the begining when out of bounds
+void st7565_advance_page(bool clearPageRemainder);
+
+// Moves the cursor forward 1 character length
+// Advance page if there is not enough room for the next character
+// Wraps to the begining when out of bounds
+void st7565_advance_char(void);
+
+// Writes a single character to the buffer at current cursor position
+// Advances the cursor while writing, inverts the pixels if true
+// Main handler that writes character data to the display buffer
+void st7565_write_char(const char data, bool invert);
+
+// Writes a string to the buffer at current cursor position
+// Advances the cursor while writing, inverts the pixels if true
+void st7565_write(const char *data, bool invert);
+
+// Writes a string to the buffer at current cursor position
+// Advances the cursor while writing, inverts the pixels if true
+// Advances the cursor to the next page, wiring ' ' to the remainder of the current page
+void st7565_write_ln(const char *data, bool invert);
+
+// Pans the buffer to the right (or left by passing true) by moving contents of the buffer
+// Useful for moving the screen in preparation for new drawing
+void st7565_pan(bool left);
+
+// Returns a pointer to the requested start index in the buffer plus remaining
+// buffer length as struct
+display_buffer_reader_t st7565_read_raw(uint16_t start_index);
+
+// Writes a string to the buffer at current cursor position
+void st7565_write_raw(const char *data, uint16_t size);
+
+// Writes a single byte into the buffer at the specified index
+void st7565_write_raw_byte(const char data, uint16_t index);
+
+// Sets a specific pixel on or off
+// Coordinates start at top-left and go right and down for positive x and y
+void st7565_write_pixel(uint8_t x, uint8_t y, bool on);
+
+// Writes a PROGMEM string to the buffer at current cursor position
+// Advances the cursor while writing, inverts the pixels if true
+// Remapped to call 'void st7565_write(const char *data, bool invert);' on ARM
+void st7565_write_P(const char *data, bool invert);
+
+// Writes a PROGMEM string to the buffer at current cursor position
+// Advances the cursor while writing, inverts the pixels if true
+// Advances the cursor to the next page, wiring ' ' to the remainder of the current page
+// Remapped to call 'void st7565_write_ln(const char *data, bool invert);' on ARM
+void st7565_write_ln_P(const char *data, bool invert);
+
+// Writes a PROGMEM string to the buffer at current cursor position
+void st7565_write_raw_P(const char *data, uint16_t size);
+
+// Can be used to manually turn on the screen if it is off
+// Returns true if the screen was on or turns on
+bool st7565_on(void);
+
+// Called when st7565_on() turns on the screen, weak function overridable by the user
+// Not called if the screen is already on
+void st7565_on_user(void);
+
+// Can be used to manually turn off the screen if it is on
+// Returns true if the screen was off or turns off
+bool st7565_off(void);
+
+// Called when st7565_off() turns off the screen, weak function overridable by the user
+// Not called if the screen is already off
+void st7565_off_user(void);
+
+// Returns true if the screen is currently on, false if it is
+// not
+bool st7565_is_on(void);
+
+// Basically it's st7565_render, but with timeout management and st7565_task_user calling!
+void st7565_task(void);
+
+// Called at the start of st7565_task, weak function overridable by the user
+void st7565_task_user(void);
+
+// Inverts the display
+// Returns true if the screen was or is inverted
+bool st7565_invert(bool invert);
+
+// Returns the maximum number of characters that will fit on a line
+uint8_t st7565_max_chars(void);
+
+// Returns the maximum number of lines that will fit on the display
+uint8_t st7565_max_lines(void);
+```
diff --git a/docs/ja/compatible_microcontrollers.md b/docs/ja/compatible_microcontrollers.md
index b675b038d2..761a4cda46 100644
--- a/docs/ja/compatible_microcontrollers.md
+++ b/docs/ja/compatible_microcontrollers.md
@@ -36,6 +36,8 @@ QMK は十分な容量のフラッシュメモリを備えた USB 対応 AVR ま
* [STM32F446](https://www.st.com/en/microcontrollers-microprocessors/stm32f446.html)
* [STM32G431](https://www.st.com/en/microcontrollers-microprocessors/stm32g4x1.html)
* [STM32G474](https://www.st.com/en/microcontrollers-microprocessors/stm32g4x4.html)
+* [STM32L412](https://www.st.com/en/microcontrollers-microprocessors/stm32l4x2.html)
+* [STM32L422](https://www.st.com/en/microcontrollers-microprocessors/stm32l4x2.html)
* [STM32L433](https://www.st.com/en/microcontrollers-microprocessors/stm32l4x3.html)
* [STM32L443](https://www.st.com/en/microcontrollers-microprocessors/stm32l4x3.html)
diff --git a/docs/ja/feature_dip_switch.md b/docs/ja/feature_dip_switch.md
index a0f6aeb003..a5436779f1 100644
--- a/docs/ja/feature_dip_switch.md
+++ b/docs/ja/feature_dip_switch.md
@@ -28,8 +28,9 @@ DIP スイッチは、以下を `rules.mk` に追加することでサポート
コールバック関数を `<keyboard>.c` に記述することができます:
```c
-void dip_switch_update_kb(uint8_t index, bool active) {
- dip_switch_update_user(index, active);
+bool dip_switch_update_kb(uint8_t index, bool active) {
+ if !(dip_switch_update_user(index, active)) { return false; }
+ return true;
}
```
@@ -37,7 +38,7 @@ void dip_switch_update_kb(uint8_t index, bool active) {
あるいは `keymap.c` に記述することもできます:
```c
-void dip_switch_update_user(uint8_t index, bool active) {
+bool dip_switch_update_user(uint8_t index, bool active) {
switch (index) {
case 0:
if(active) { audio_on(); } else { audio_off(); }
@@ -62,6 +63,7 @@ void dip_switch_update_user(uint8_t index, bool active) {
}
break;
}
+ return true;
}
```
@@ -69,8 +71,9 @@ void dip_switch_update_user(uint8_t index, bool active) {
```c
-void dip_switch_update_mask_kb(uint32_t state) {
- dip_switch_update_mask_user(state);
+bool dip_switch_update_mask_kb(uint32_t state) {
+ if (!dip_switch_update_mask_user(state)) { return false; }
+ return true;
}
```
@@ -78,7 +81,7 @@ void dip_switch_update_mask_kb(uint32_t state) {
あるいは `keymap.c` に記述することもできます:
```c
-void dip_switch_update_mask_user(uint32_t state) {
+bool dip_switch_update_mask_user(uint32_t state) {
if (state & (1UL<<0) && state & (1UL<<1)) {
layer_on(_ADJUST); // C on esc
} else {
@@ -94,6 +97,7 @@ void dip_switch_update_mask_user(uint32_t state) {
} else {
layer_off(_TEST_B);
}
+ return true;
}
```
diff --git a/docs/serial_driver.md b/docs/serial_driver.md
index 359fc59551..ed989b0a15 100644
--- a/docs/serial_driver.md
+++ b/docs/serial_driver.md
@@ -73,7 +73,7 @@ You must also enable the ChibiOS `SERIAL` feature:
Do note that the configuration required is for the `SERIAL` peripheral, not the `UART` peripheral.
### USART Full-duplex
-Targeting STM32 boards where communication is offloaded to a USART hardware device. The advantage over bitbang is that this provides fast and accurate timings. USART Full-Duplex requires two conductors **without** pull-up resistors instead of one conductor with a pull-up resistor unlike the Half-duplex driver, but it is more efficent as it uses DMA transfers, which can result in even faster transmission speeds.
+Targeting STM32 boards where communication is offloaded to a USART hardware device. The advantage over bitbang is that this provides fast and accurate timings. USART Full-Duplex requires two conductors **without** pull-up resistors instead of one conductor with a pull-up resistor unlike the Half-duplex driver. Due to its internal design it is more efficent, which can result in even faster transmission speeds.
#### Pin configuration
@@ -86,12 +86,13 @@ Please note that `TX` of the master half has to be connected with the `RX` pin o
To use the driver, add this to your rules.mk:
```make
-SERIAL_DRIVER = usart_duplex
+SERIAL_DRIVER = usart
```
Next configure the hardware via your config.h:
```c
+#define SERIAL_USART_FULL_DUPLEX // Enable full duplex operation mode.
#define SERIAL_USART_TX_PIN B6 // USART TX pin
#define SERIAL_USART_RX_PIN B7 // USART RX pin
//#define USART1_REMAP // Remap USART TX and RX pins on STM32F103 MCUs, see table below.
@@ -104,17 +105,17 @@ Next configure the hardware via your config.h:
// 3: 57600 baud
// 4: 38400 baud
// 5: 19200 baud
-#define SERIAL_USART_DRIVER UARTD1 // USART driver of TX and RX pin. default: UARTD1
+#define SERIAL_USART_DRIVER SD1 // USART driver of TX and RX pin. default: SD1
#define SERIAL_USART_TX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7
#define SERIAL_USART_RX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7
#define SERIAL_USART_TIMEOUT 100 // USART driver timeout. default 100
```
-You must also enable the ChibiOS `UART` with blocking api feature:
-* In your board's halconf.h: `#define HAL_USE_UART TRUE` and `#define UART_USE_WAIT TRUE`
-* In your board's mcuconf.h: `#define STM32_UART_USE_USARTn TRUE` (where 'n' matches the peripheral number of your selected USART on the MCU)
+You must also enable the ChibiOS `SERIAL` feature:
+* In your board's halconf.h: `#define HAL_USE_SERIAL TRUE`
+* In your board's mcuconf.h: `#define STM32_SERIAL_USE_USARTn TRUE` (where 'n' matches the peripheral number of your selected USART on the MCU)
-Do note that the configuration required is for the `UART` peripheral, not the `SERIAL` peripheral.
+Do note that the configuration required is for the `SERIAL` peripheral, not the `UART` peripheral.
#### Pins for USART Peripherals with Alternate Functions for selected STM32 MCUs
diff --git a/docs/syllabus.md b/docs/syllabus.md
index ec7f66ba78..b33bd9e727 100644
--- a/docs/syllabus.md
+++ b/docs/syllabus.md
@@ -40,6 +40,7 @@ These topics start to dig into some of the features that QMK supports. You don't
* [Tap Dance](feature_tap_dance.md)
* [Combos](feature_combo.md)
* [Userspace](feature_userspace.md)
+ * [Key Overrides](feature_key_overrides.md)
# Advanced Topics
diff --git a/docs/understanding_qmk.md b/docs/understanding_qmk.md
index 331b1c893c..e3dd5cb780 100644
--- a/docs/understanding_qmk.md
+++ b/docs/understanding_qmk.md
@@ -146,6 +146,7 @@ The `process_record()` function itself is deceptively simple, but hidden within
* [`bool process_audio(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/e1203a222bb12ab9733916164a000ef3ac48da93/quantum/process_keycode/process_audio.c#L19)
* [`bool process_steno(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/e1203a222bb12ab9733916164a000ef3ac48da93/quantum/process_keycode/process_steno.c#L160)
* [`bool process_music(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/e1203a222bb12ab9733916164a000ef3ac48da93/quantum/process_keycode/process_music.c#L114)
+ * [`bool process_key_override(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/5a1b857dea45a17698f6baa7dd1b7a7ea907fb0a/quantum/process_keycode/process_key_override.c#L397)
* [`bool process_tap_dance(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/e1203a222bb12ab9733916164a000ef3ac48da93/quantum/process_keycode/process_tap_dance.c#L141)
* [`bool process_unicode_common(uint16_t keycode, keyrecord_t *record)`](https://github.com/qmk/qmk_firmware/blob/e1203a222bb12ab9733916164a000ef3ac48da93/quantum/process_keycode/process_unicode_common.c#L169)
calls one of:
diff --git a/docs/zh-cn/custom_quantum_functions.md b/docs/zh-cn/custom_quantum_functions.md
index 27b2edf38f..44cb5cf76d 100644
--- a/docs/zh-cn/custom_quantum_functions.md
+++ b/docs/zh-cn/custom_quantum_functions.md
@@ -297,7 +297,7 @@ void suspend_wakeup_init_user(void) {
本例使用了Planck键盘示范了如何设置 [RGB背光灯](feature_rgblight.md)使之与层对应
```c
-uint32_t layer_state_set_user(uint32_t state) {
+layer_state_t layer_state_set_user(layer_state_t state) {
switch (biton32(state)) {
case _RAISE:
rgblight_setrgb (0x00, 0x00, 0xFF);
@@ -321,7 +321,7 @@ uint32_t layer_state_set_user(uint32_t state) {
### `layer_state_set_*` 函数文档
* 键盘/修订: `uint32_t layer_state_set_kb(uint32_t state)`
-* 布局: `uint32_t layer_state_set_user(uint32_t state)`
+* 布局: `layer_state_t layer_state_set_user(layer_state_t state)`
该`状态`是活动层的bitmask, 详见[布局概述](keymap.md#布局的层状态)
@@ -377,7 +377,7 @@ void keyboard_post_init_user(void) {
以上函数会在读EEPROM配置后立即使用该设置来设置默认层RGB颜色。"raw"的值是从你上面基于"union"创建的结构体中转换来的。
```c
-uint32_t layer_state_set_user(uint32_t state) {
+layer_state_t layer_state_set_user(layer_state_t state) {
switch (biton32(state)) {
case _RAISE:
if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom_magenta(); rgblight_mode_noeeprom(1); }