From 78a0adfbb4d2c4e12f93f2a62ded0020d406243e Mon Sep 17 00:00:00 2001 From: Ryan Date: Sun, 2 Jun 2024 12:42:24 +1000 Subject: [docs] Organize driver & feature docs into subfolders (#23848) Co-authored-by: Nick Brassel --- docs/ChangeLog/20210828.md | 4 +- docs/ChangeLog/20211127.md | 4 +- docs/ChangeLog/20220226.md | 2 +- docs/ChangeLog/20220528.md | 4 +- docs/ChangeLog/20220827.md | 2 +- docs/ChangeLog/20221126.md | 4 +- docs/ChangeLog/20230226.md | 2 +- docs/ChangeLog/20230528.md | 4 +- docs/ChangeLog/20230827.md | 2 +- docs/ChangeLog/20240225.md | 2 +- docs/_sidebar.json | 112 ++-- docs/adc_driver.md | 173 ------ docs/apa102_driver.md | 49 -- docs/audio_driver.md | 239 -------- docs/cli_commands.md | 2 +- docs/config_options.md | 12 +- docs/custom_quantum_functions.md | 2 +- docs/documentation_best_practices.md | 2 +- docs/driver_installation_zadig.md | 4 +- docs/drivers/adc.md | 173 ++++++ docs/drivers/apa102.md | 49 ++ docs/drivers/audio.md | 239 ++++++++ docs/drivers/eeprom.md | 180 ++++++ docs/drivers/flash.md | 26 + docs/drivers/gpio.md | 44 ++ docs/drivers/i2c.md | 290 +++++++++ docs/drivers/serial.md | 397 ++++++++++++ docs/drivers/spi.md | 167 +++++ docs/drivers/uart.md | 122 ++++ docs/drivers/ws2812.md | 253 ++++++++ docs/easy_maker.md | 2 +- docs/eeprom_driver.md | 180 ------ docs/faq_build.md | 2 +- docs/faq_debug.md | 2 +- docs/faq_keymap.md | 6 +- docs/feature_advanced_keycodes.md | 4 +- docs/feature_audio.md | 377 ------------ docs/feature_auto_shift.md | 398 ------------ docs/feature_autocorrect.md | 324 ---------- docs/feature_backlight.md | 309 ---------- docs/feature_bluetooth.md | 46 -- docs/feature_bootmagic.md | 85 --- docs/feature_caps_word.md | 189 ------ docs/feature_combo.md | 398 ------------ docs/feature_command.md | 51 -- docs/feature_converters.md | 16 +- docs/feature_digitizer.md | 119 ---- docs/feature_dip_switch.md | 132 ---- docs/feature_dynamic_macros.md | 69 --- docs/feature_eeprom.md | 2 +- docs/feature_encoders.md | 186 ------ docs/feature_grave_esc.md | 32 - docs/feature_haptic_feedback.md | 214 ------- docs/feature_hd44780.md | 298 --------- docs/feature_joystick.md | 222 ------- docs/feature_key_lock.md | 23 - docs/feature_key_overrides.md | 256 -------- docs/feature_layers.md | 4 +- docs/feature_leader_key.md | 297 --------- docs/feature_led_indicators.md | 127 ---- docs/feature_led_matrix.md | 455 -------------- docs/feature_macros.md | 4 +- docs/feature_midi.md | 264 -------- docs/feature_mouse_keys.md | 208 ------- docs/feature_oled_driver.md | 499 --------------- docs/feature_os_detection.md | 132 ---- docs/feature_pointing_device.md | 928 ---------------------------- docs/feature_programmable_button.md | 146 ----- docs/feature_ps2_mouse.md | 349 ----------- docs/feature_rawhid.md | 158 ----- docs/feature_repeat_key.md | 459 -------------- docs/feature_rgb_matrix.md | 1119 ---------------------------------- docs/feature_rgblight.md | 581 ------------------ docs/feature_secure.md | 56 -- docs/feature_send_string.md | 226 ------- docs/feature_sequencer.md | 89 --- docs/feature_space_cadet.md | 60 -- docs/feature_split_keyboard.md | 508 --------------- docs/feature_st7565.md | 274 --------- docs/feature_stenography.md | 221 ------- docs/feature_swap_hands.md | 57 -- docs/feature_tap_dance.md | 567 ----------------- docs/feature_tri_layer.md | 50 -- docs/feature_unicode.md | 454 -------------- docs/feature_wpm.md | 76 --- docs/features/audio.md | 377 ++++++++++++ docs/features/auto_shift.md | 398 ++++++++++++ docs/features/autocorrect.md | 324 ++++++++++ docs/features/backlight.md | 309 ++++++++++ docs/features/bluetooth.md | 46 ++ docs/features/bootmagic.md | 85 +++ docs/features/caps_word.md | 189 ++++++ docs/features/combo.md | 398 ++++++++++++ docs/features/command.md | 51 ++ docs/features/digitizer.md | 119 ++++ docs/features/dip_switch.md | 132 ++++ docs/features/dynamic_macros.md | 69 +++ docs/features/encoders.md | 186 ++++++ docs/features/grave_esc.md | 32 + docs/features/haptic_feedback.md | 214 +++++++ docs/features/hd44780.md | 298 +++++++++ docs/features/joystick.md | 222 +++++++ docs/features/key_lock.md | 23 + docs/features/key_overrides.md | 256 ++++++++ docs/features/leader_key.md | 297 +++++++++ docs/features/led_indicators.md | 127 ++++ docs/features/led_matrix.md | 455 ++++++++++++++ docs/features/midi.md | 264 ++++++++ docs/features/mouse_keys.md | 208 +++++++ docs/features/oled_driver.md | 499 +++++++++++++++ docs/features/os_detection.md | 132 ++++ docs/features/pointing_device.md | 928 ++++++++++++++++++++++++++++ docs/features/programmable_button.md | 146 +++++ docs/features/ps2_mouse.md | 349 +++++++++++ docs/features/rawhid.md | 158 +++++ docs/features/repeat_key.md | 456 ++++++++++++++ docs/features/rgb_matrix.md | 1119 ++++++++++++++++++++++++++++++++++ docs/features/rgblight.md | 581 ++++++++++++++++++ docs/features/secure.md | 56 ++ docs/features/send_string.md | 226 +++++++ docs/features/sequencer.md | 89 +++ docs/features/space_cadet.md | 60 ++ docs/features/split_keyboard.md | 508 +++++++++++++++ docs/features/st7565.md | 274 +++++++++ docs/features/stenography.md | 221 +++++++ docs/features/swap_hands.md | 57 ++ docs/features/tap_dance.md | 567 +++++++++++++++++ docs/features/tri_layer.md | 50 ++ docs/features/unicode.md | 454 ++++++++++++++ docs/features/wpm.md | 76 +++ docs/flash_driver.md | 26 - docs/flashing.md | 4 +- docs/getting_started_make_guide.md | 6 +- docs/gpio_control.md | 44 -- docs/hand_wire.md | 2 +- docs/hardware_drivers.md | 10 +- docs/hardware_keyboard_guidelines.md | 2 +- docs/i2c_driver.md | 290 --------- docs/keycodes.md | 42 +- docs/mod_tap.md | 2 +- docs/newbs_building_firmware.md | 4 +- docs/platformdev_rp2040.md | 30 +- docs/porting_your_keyboard_to_qmk.md | 2 +- docs/pr_checklist.md | 6 +- docs/ref_functions.md | 4 +- docs/reference_glossary.md | 12 +- docs/reference_info_json.md | 44 +- docs/serial_driver.md | 397 ------------ docs/spi_driver.md | 167 ----- docs/syllabus.md | 28 +- docs/tap_hold.md | 2 +- docs/uart_driver.md | 122 ---- docs/ws2812_driver.md | 253 -------- 153 files changed, 14228 insertions(+), 14231 deletions(-) delete mode 100644 docs/adc_driver.md delete mode 100644 docs/apa102_driver.md delete mode 100644 docs/audio_driver.md create mode 100644 docs/drivers/adc.md create mode 100644 docs/drivers/apa102.md create mode 100644 docs/drivers/audio.md create mode 100644 docs/drivers/eeprom.md create mode 100644 docs/drivers/flash.md create mode 100644 docs/drivers/gpio.md create mode 100644 docs/drivers/i2c.md create mode 100644 docs/drivers/serial.md create mode 100644 docs/drivers/spi.md create mode 100644 docs/drivers/uart.md create mode 100644 docs/drivers/ws2812.md delete mode 100644 docs/eeprom_driver.md delete mode 100644 docs/feature_audio.md delete mode 100644 docs/feature_auto_shift.md delete mode 100644 docs/feature_autocorrect.md delete mode 100644 docs/feature_backlight.md delete mode 100644 docs/feature_bluetooth.md delete mode 100644 docs/feature_bootmagic.md delete mode 100644 docs/feature_caps_word.md delete mode 100644 docs/feature_combo.md delete mode 100644 docs/feature_command.md delete mode 100644 docs/feature_digitizer.md delete mode 100644 docs/feature_dip_switch.md delete mode 100644 docs/feature_dynamic_macros.md delete mode 100644 docs/feature_encoders.md delete mode 100644 docs/feature_grave_esc.md delete mode 100644 docs/feature_haptic_feedback.md delete mode 100644 docs/feature_hd44780.md delete mode 100644 docs/feature_joystick.md delete mode 100644 docs/feature_key_lock.md delete mode 100644 docs/feature_key_overrides.md delete mode 100644 docs/feature_leader_key.md delete mode 100644 docs/feature_led_indicators.md delete mode 100644 docs/feature_led_matrix.md delete mode 100644 docs/feature_midi.md delete mode 100644 docs/feature_mouse_keys.md delete mode 100644 docs/feature_oled_driver.md delete mode 100644 docs/feature_os_detection.md delete mode 100644 docs/feature_pointing_device.md delete mode 100644 docs/feature_programmable_button.md delete mode 100644 docs/feature_ps2_mouse.md delete mode 100644 docs/feature_rawhid.md delete mode 100644 docs/feature_repeat_key.md delete mode 100644 docs/feature_rgb_matrix.md delete mode 100644 docs/feature_rgblight.md delete mode 100644 docs/feature_secure.md delete mode 100644 docs/feature_send_string.md delete mode 100644 docs/feature_sequencer.md delete mode 100644 docs/feature_space_cadet.md delete mode 100644 docs/feature_split_keyboard.md delete mode 100644 docs/feature_st7565.md delete mode 100644 docs/feature_stenography.md delete mode 100644 docs/feature_swap_hands.md delete mode 100644 docs/feature_tap_dance.md delete mode 100644 docs/feature_tri_layer.md delete mode 100644 docs/feature_unicode.md delete mode 100644 docs/feature_wpm.md create mode 100644 docs/features/audio.md create mode 100644 docs/features/auto_shift.md create mode 100644 docs/features/autocorrect.md create mode 100644 docs/features/backlight.md create mode 100644 docs/features/bluetooth.md create mode 100644 docs/features/bootmagic.md create mode 100644 docs/features/caps_word.md create mode 100644 docs/features/combo.md create mode 100644 docs/features/command.md create mode 100644 docs/features/digitizer.md create mode 100644 docs/features/dip_switch.md create mode 100644 docs/features/dynamic_macros.md create mode 100644 docs/features/encoders.md create mode 100644 docs/features/grave_esc.md create mode 100644 docs/features/haptic_feedback.md create mode 100644 docs/features/hd44780.md create mode 100644 docs/features/joystick.md create mode 100644 docs/features/key_lock.md create mode 100644 docs/features/key_overrides.md create mode 100644 docs/features/leader_key.md create mode 100644 docs/features/led_indicators.md create mode 100644 docs/features/led_matrix.md create mode 100644 docs/features/midi.md create mode 100644 docs/features/mouse_keys.md create mode 100644 docs/features/oled_driver.md create mode 100644 docs/features/os_detection.md create mode 100644 docs/features/pointing_device.md create mode 100644 docs/features/programmable_button.md create mode 100644 docs/features/ps2_mouse.md create mode 100644 docs/features/rawhid.md create mode 100644 docs/features/repeat_key.md create mode 100644 docs/features/rgb_matrix.md create mode 100644 docs/features/rgblight.md create mode 100644 docs/features/secure.md create mode 100644 docs/features/send_string.md create mode 100644 docs/features/sequencer.md create mode 100644 docs/features/space_cadet.md create mode 100644 docs/features/split_keyboard.md create mode 100644 docs/features/st7565.md create mode 100644 docs/features/stenography.md create mode 100644 docs/features/swap_hands.md create mode 100644 docs/features/tap_dance.md create mode 100644 docs/features/tri_layer.md create mode 100644 docs/features/unicode.md create mode 100644 docs/features/wpm.md delete mode 100644 docs/flash_driver.md delete mode 100644 docs/gpio_control.md delete mode 100644 docs/i2c_driver.md delete mode 100644 docs/serial_driver.md delete mode 100644 docs/spi_driver.md delete mode 100644 docs/uart_driver.md delete mode 100644 docs/ws2812_driver.md diff --git a/docs/ChangeLog/20210828.md b/docs/ChangeLog/20210828.md index f84169cc94..18b1d92b0c 100644 --- a/docs/ChangeLog/20210828.md +++ b/docs/ChangeLog/20210828.md @@ -10,11 +10,11 @@ It is also now possible to define combos that have keys overlapping with other c ### Key Overrides ([#11422](https://github.com/qmk/qmk_firmware/pull/11422)) {#key-overrides} -QMK now has a new feature: [key overrides](../feature_key_overrides). This feature allows for overriding the output of key combinations involving modifiers. As an example, pressing Shift+2 normally results in an @ on US-ANSI keyboard layouts -- the new key overrides allow for adding similar functionality, but for any modifier + key press. +QMK now has a new feature: [key overrides](../features/key_overrides). This feature allows for overriding the output of key combinations involving modifiers. As an example, pressing Shift+2 normally results in an @ on US-ANSI keyboard layouts -- the new key overrides allow for adding similar functionality, but for any modifier + key press. To illustrate, it's now possible to use the key overrides feature to translate Shift + Backspace into Delete -- an often-requested example of where this functionality comes in handy. -There's far more to describe that what lives in this changelog, so head over to the [key overrides documentation](../feature_key_overrides) for more examples and info. +There's far more to describe that what lives in this changelog, so head over to the [key overrides documentation](../features/key_overrides) for more examples and info. ### Digitizer support ([#12851](https://github.com/qmk/qmk_firmware/pull/12851)) diff --git a/docs/ChangeLog/20211127.md b/docs/ChangeLog/20211127.md index d810be505a..5ca68c3999 100644 --- a/docs/ChangeLog/20211127.md +++ b/docs/ChangeLog/20211127.md @@ -31,7 +31,7 @@ QMK now has core-supplied support for the following pointing device peripherals: | `POINTING_DEVICE_DRIVER = pimoroni_trackball` | Pimoroni Trackball | | `POINTING_DEVICE_DRIVER = pmw3360` | PMW 3360 | -See the new documentation for the [Pointing Device](../feature_pointing_device) feature for more information on specific configuration for each driver. +See the new documentation for the [Pointing Device](../features/pointing_device) feature for more information on specific configuration for each driver. ### Dynamic Tapping Term ([#11036](https://github.com/qmk/qmk_firmware/pull/11036)) {#dynamic-tapping-term} @@ -116,7 +116,7 @@ Related to the previous section -- RGB Matrix modes have now been made to be opt Most keyboards keep their original functionality, but over time the QMK maintainers have found that removal of animations ends up being the quickest way to free up space... and some keyboards have had animations such as reactive effects disabled by default in order to still fit within the flash space available. -The full list of configurables to turn specific animations back on can be found at on the [RGB Matrix documentation](../feature_rgb_matrix#rgb-matrix-effects) page. +The full list of configurables to turn specific animations back on can be found at on the [RGB Matrix documentation](../features/rgb_matrix#rgb-matrix-effects) page. ### OLED task refactoring ([#14864](https://github.com/qmk/qmk_firmware/pull/14864)) {#oled-task-refactor} diff --git a/docs/ChangeLog/20220226.md b/docs/ChangeLog/20220226.md index f0cbbc0603..a10b6447ea 100644 --- a/docs/ChangeLog/20220226.md +++ b/docs/ChangeLog/20220226.md @@ -12,7 +12,7 @@ Something something *Lets go gamers!* Pointing devices can now be shared across a split keyboard with support for a single pointing device or a pointing device on each side. -See the [Pointing Device](../feature_pointing_device) documentation for further configuration options. +See the [Pointing Device](../features/pointing_device) documentation for further configuration options. ## Changes Requiring User Action {#changes-requiring-user-action} diff --git a/docs/ChangeLog/20220528.md b/docs/ChangeLog/20220528.md index 31347c9c00..ae84f163d4 100644 --- a/docs/ChangeLog/20220528.md +++ b/docs/ChangeLog/20220528.md @@ -8,7 +8,7 @@ This is a new feature that allows for capslock-like functionality that turns its For instance, if you wish to type "QMK" without holding shift the entire time, you can either tap both left and right shift, or double-tap shift, to turn on _Caps Word_ -- then type `qmk` (lowercase) without holding shift. Once you hit any key other than `a`--`z`, `0`--`9`, `-`, `_`, delete, or backspace, this will go back to normal typing! -There are other activation mechanisms as well as configurable options like timeout and the like -- see the [Caps Word documentation](../feature_caps_word) for more information. +There are other activation mechanisms as well as configurable options like timeout and the like -- see the [Caps Word documentation](../features/caps_word) for more information. ### Quantum Painter ([#10174](https://github.com/qmk/qmk_firmware/pull/10174)) {#quantum-painter} @@ -26,7 +26,7 @@ Quantum Painter is not supported on AVR due to complexity and size constraints. ### Encoder Mapping ([#13286](https://github.com/qmk/qmk_firmware/pull/13286)) {#encoder-mapping} -One of the long-standing complaints with Encoders is that there has been no easy way to configure them in user keymaps. [#13286](https://github.com/qmk/qmk_firmware/pull/13286) added support for [Encoder Mapping](../feature_encoders#encoder-map), which allows users to define encoder functionality in a similar way to their normal keymap. +One of the long-standing complaints with Encoders is that there has been no easy way to configure them in user keymaps. [#13286](https://github.com/qmk/qmk_firmware/pull/13286) added support for [Encoder Mapping](../features/encoders#encoder-map), which allows users to define encoder functionality in a similar way to their normal keymap. ::: warning This is not yet supported by QMK Configurator. It is also unlikely to ever be supported by VIA. diff --git a/docs/ChangeLog/20220827.md b/docs/ChangeLog/20220827.md index d58db91272..6d9f82f36a 100644 --- a/docs/ChangeLog/20220827.md +++ b/docs/ChangeLog/20220827.md @@ -83,7 +83,7 @@ The now-EOL kbfirmware allowed people who aren't set up with QMK the ability to QMK has had the ability to write to internal MCU flash in order to emulate EEPROM for some time now, but it was only limited to a small number of MCUs. The base HAL used by QMK for a large number of ARM devices provides a "proper" embedded MCU flash driver, so _@tzarc_ decoupled the wear-leveling algorithm from the old flash writing code, improved it, wrote some tests, and enabled its use for a much larger number of other devices... including RP2040's XIP flash, and external SPI NOR Flash. -See the [EEPROM Driver](../eeprom_driver) documentation for more information. +See the [EEPROM Driver](../drivers/eeprom) documentation for more information. ### Pointing Device Improvements ([#16371](https://github.com/qmk/qmk_firmware/pull/16371), [#17111](https://github.com/qmk/qmk_firmware/pull/17111), [#17176](https://github.com/qmk/qmk_firmware/pull/17176), [#17482](https://github.com/qmk/qmk_firmware/pull/17482), [#17776](https://github.com/qmk/qmk_firmware/pull/17776), [#17613](https://github.com/qmk/qmk_firmware/pull/17613)) {#pointing-device-improvements} diff --git a/docs/ChangeLog/20221126.md b/docs/ChangeLog/20221126.md index 25cf1d592d..41b0ad0a6b 100644 --- a/docs/ChangeLog/20221126.md +++ b/docs/ChangeLog/20221126.md @@ -4,7 +4,7 @@ ### Autocorrect ([#15699](https://github.com/qmk/qmk_firmware/pull/15699)) {#autocorrect} -_@getreuer_ in their infinite wisdom decided that autocorrect was a feature needed by QMK. As is customary, _@drashna_ adapted it to core and got it into a state that everyone else can use it. See [Feature: Autocorrect](../feature_autocorrect) for more ifnormation (grin). +_@getreuer_ in their infinite wisdom decided that autocorrect was a feature needed by QMK. As is customary, _@drashna_ adapted it to core and got it into a state that everyone else can use it. See [Feature: Autocorrect](../features/autocorrect) for more ifnormation (grin). ## Changes Requiring User Action {#changes-requiring-user-action} @@ -132,7 +132,7 @@ The equivalent transformations should be done for LED Matrix boards. ### Unicode mode refactoring {#unicode-mode-renaming} -Unicode modes were renamed in order to prevent collision with equivalent keycodes. The available values for `UNICODE_SELECTED_MODES` changed -- see [Feature: Unicode](../feature_unicode#setting-the-input-mode) for the new list of values and how to configure them. +Unicode modes were renamed in order to prevent collision with equivalent keycodes. The available values for `UNICODE_SELECTED_MODES` changed -- see [Feature: Unicode](../features/unicode#setting-the-input-mode) for the new list of values and how to configure them. ## Notable core changes {#notable-core} diff --git a/docs/ChangeLog/20230226.md b/docs/ChangeLog/20230226.md index ee56068604..87ff924264 100644 --- a/docs/ChangeLog/20230226.md +++ b/docs/ChangeLog/20230226.md @@ -106,7 +106,7 @@ void leader_end_user(void) { } ``` -For more information please see the [Leader Key documentation](../feature_leader_key). +For more information please see the [Leader Key documentation](../features/leader_key). ### Updated Keyboard Codebases {#updated-keyboard-codebases} diff --git a/docs/ChangeLog/20230528.md b/docs/ChangeLog/20230528.md index 40ab3a420c..77ad6209b0 100644 --- a/docs/ChangeLog/20230528.md +++ b/docs/ChangeLog/20230528.md @@ -24,7 +24,7 @@ Of note for keyboard designers: A new pair of keys has been added to QMK -- namely `QK_REPEAT_KEY` and `QK_ALT_REPEAT_KEY` (shortened: `QK_REP`/`QK_AREP`). These allow you to repeat the last key pressed, or in the case of the alternate key, press the "opposite" of the last key. For example, if you press `KC_LEFT`, pressing `QK_REPEAT_KEY` afterwards repeats `KC_LEFT`, but pressing `QK_ALT_REPEAT_KEY` instead sends `KC_RIGHT`. -The full list of default alternate keys is available on the [Repeat Key](../feature_repeat_key) documentation. +The full list of default alternate keys is available on the [Repeat Key](../features/repeat_key) documentation. To enable these keys, in your keymap's `rules.mk`, add: @@ -93,7 +93,7 @@ Additionally, this ensures that builds on QMK Configurator produce some sort of The "classic" OLED driver picked up support for additional sizes of OLED displays, support for the SH1107 controller, and SPI-based OLED support. -Other configurable items are available and can be found on the [OLED Driver page](../feature_oled_driver). +Other configurable items are available and can be found on the [OLED Driver page](../features/oled_driver). ## Full changelist {#full-changelist} diff --git a/docs/ChangeLog/20230827.md b/docs/ChangeLog/20230827.md index aecbcb0d8f..493ee84349 100644 --- a/docs/ChangeLog/20230827.md +++ b/docs/ChangeLog/20230827.md @@ -42,7 +42,7 @@ AVR sees minimal (if any) benefit -- `double` was interpreted as `float` on AVR ### Remove encoder in-matrix workaround code ([#20389](https://github.com/qmk/qmk_firmware/pull/20389)) {#remove-encoder-in-matrix-workaround-code} -Some keyboards "hacked" encoder support into spare slots in the key matrix in order to interoperate with VIA. This workaround is no longer necessary, and the code has been removed. If you have a keyboard that uses this workaround, you will need to update your keymap to use the new [Encoder Map](../feature_encoders#encoder-map) API instead. +Some keyboards "hacked" encoder support into spare slots in the key matrix in order to interoperate with VIA. This workaround is no longer necessary, and the code has been removed. If you have a keyboard that uses this workaround, you will need to update your keymap to use the new [Encoder Map](../features/encoders#encoder-map) API instead. ### Unicodemap keycodes rename ([#21092](https://github.com/qmk/qmk_firmware/pull/21092)) {#unicodemap-keycodes-rename} diff --git a/docs/ChangeLog/20240225.md b/docs/ChangeLog/20240225.md index f4103c594e..1ebfbd2309 100644 --- a/docs/ChangeLog/20240225.md +++ b/docs/ChangeLog/20240225.md @@ -120,7 +120,7 @@ In some cases, accidental automatic activation of the mouse layer made it diffic ### DIP Switch Mapping ([#22543](https://github.com/qmk/qmk_firmware/pull/22543)) {#dip-switch-map} -Much like Encoder Mapping, DIP Switch Mapping allows for specifying a table of actions to execute when a DIP switch state changes. See the [DIP Switch Documentation](../feature_dip_switch#dip-switch-map) for more information. +Much like Encoder Mapping, DIP Switch Mapping allows for specifying a table of actions to execute when a DIP switch state changes. See the [DIP Switch Documentation](../features/dip_switch#dip-switch-map) for more information. ```c #if defined(DIP_SWITCH_MAP_ENABLE) diff --git a/docs/_sidebar.json b/docs/_sidebar.json index f1b7c156e6..b41719e4b3 100644 --- a/docs/_sidebar.json +++ b/docs/_sidebar.json @@ -103,45 +103,45 @@ { "text": "Advanced Keycodes", "items": [ - { "text": "Command", "link": "/feature_command" }, - { "text": "Dynamic Macros", "link": "/feature_dynamic_macros" }, - { "text": "Grave Escape", "link": "/feature_grave_esc" }, - { "text": "Leader Key", "link": "/feature_leader_key" }, + { "text": "Command", "link": "/features/command" }, + { "text": "Dynamic Macros", "link": "/features/dynamic_macros" }, + { "text": "Grave Escape", "link": "/features/grave_esc" }, + { "text": "Leader Key", "link": "/features/leader_key" }, { "text": "Mod-Tap", "link": "/mod_tap" }, { "text": "Macros", "link": "/feature_macros" }, - { "text": "Mouse Keys", "link": "/feature_mouse_keys" }, - { "text": "Programmable Button", "link": "/feature_programmable_button" }, - { "text": "Repeat Key", "link": "/feature_repeat_key" }, - { "text": "Space Cadet Shift", "link": "/feature_space_cadet" }, + { "text": "Mouse Keys", "link": "/features/mouse_keys" }, + { "text": "Programmable Button", "link": "/features/programmable_button" }, + { "text": "Repeat Key", "link": "/features/repeat_key" }, + { "text": "Space Cadet Shift", "link": "/features/space_cadet" }, { "text": "US ANSI Shifted Keys", "link": "/keycodes_us_ansi_shifted" } ] }, { "text": "Software Features", "items": [ - { "text": "Auto Shift", "link": "/feature_auto_shift" }, - { "text": "Autocorrect", "link": "/feature_autocorrect" }, - { "text": "Caps Word", "link": "/feature_caps_word" }, - { "text": "Combos", "link": "/feature_combo" }, + { "text": "Auto Shift", "link": "/features/auto_shift" }, + { "text": "Autocorrect", "link": "/features/autocorrect" }, + { "text": "Caps Word", "link": "/features/caps_word" }, + { "text": "Combos", "link": "/features/combo" }, { "text": "Debounce API", "link": "/feature_debounce_type" }, - { "text": "Digitizer", "link": "/feature_digitizer" }, + { "text": "Digitizer", "link": "/features/digitizer" }, { "text": "EEPROM", "link": "/feature_eeprom" }, - { "text": "Key Lock", "link": "/feature_key_lock" }, - { "text": "Key Overrides", "link": "/feature_key_overrides" }, + { "text": "Key Lock", "link": "/features/key_lock" }, + { "text": "Key Overrides", "link": "/features/key_overrides" }, { "text": "Layers", "link": "/feature_layers" }, { "text": "One Shot Keys", "link": "/one_shot_keys" }, - { "text": "OS Detection", "link": "/feature_os_detection" }, - { "text": "Raw HID", "link": "/feature_rawhid" }, - { "text": "Secure", "link": "/feature_secure" }, - { "text": "Send String", "link": "/feature_send_string" }, - { "text": "Sequencer", "link": "/feature_sequencer" }, - { "text": "Swap Hands", "link": "/feature_swap_hands" }, - { "text": "Tap Dance", "link": "/feature_tap_dance" }, + { "text": "OS Detection", "link": "/features/os_detection" }, + { "text": "Raw HID", "link": "/features/rawhid" }, + { "text": "Secure", "link": "/features/secure" }, + { "text": "Send String", "link": "/features/send_string" }, + { "text": "Sequencer", "link": "/features/sequencer" }, + { "text": "Swap Hands", "link": "/features/swap_hands" }, + { "text": "Tap Dance", "link": "/features/tap_dance" }, { "text": "Tap-Hold Configuration", "link": "/tap_hold" }, - { "text": "Tri Layer", "link": "/feature_tri_layer" }, - { "text": "Unicode", "link": "/feature_unicode" }, + { "text": "Tri Layer", "link": "/features/tri_layer" }, + { "text": "Unicode", "link": "/features/unicode" }, { "text": "Userspace", "link": "/feature_userspace" }, - { "text": "WPM Calculation", "link": "/feature_wpm" } + { "text": "WPM Calculation", "link": "/features/wpm" } ] }, { @@ -157,35 +157,35 @@ { "text": "Quantum Painter LVGL Integration", "link": "/quantum_painter_lvgl" } ] }, - { "text": "HD44780 LCD Driver", "link": "/feature_hd44780" }, - { "text": "ST7565 LCD Driver", "link": "/feature_st7565" }, - { "text": "OLED Driver", "link": "/feature_oled_driver" } + { "text": "HD44780 LCD Driver", "link": "/features/hd44780" }, + { "text": "ST7565 LCD Driver", "link": "/features/st7565" }, + { "text": "OLED Driver", "link": "/features/oled_driver" } ] }, { "text": "Lighting", "items": [ - { "text": "Backlight", "link": "/feature_backlight" }, - { "text": "LED Matrix", "link": "/feature_led_matrix" }, - { "text": "RGB Lighting", "link": "/feature_rgblight" }, - { "text": "RGB Matrix", "link": "/feature_rgb_matrix" } + { "text": "Backlight", "link": "/features/backlight" }, + { "text": "LED Matrix", "link": "/features/led_matrix" }, + { "text": "RGB Lighting", "link": "/features/rgblight" }, + { "text": "RGB Matrix", "link": "/features/rgb_matrix" } ] }, - { "text": "Audio", "link": "/feature_audio" }, - { "text": "Bluetooth", "link": "/feature_bluetooth" }, - { "text": "Bootmagic Lite", "link": "/feature_bootmagic" }, + { "text": "Audio", "link": "/features/audio" }, + { "text": "Bluetooth", "link": "/features/bluetooth" }, + { "text": "Bootmagic Lite", "link": "/features/bootmagic" }, { "text": "Converters", "link": "/feature_converters" }, { "text": "Custom Matrix", "link": "/custom_matrix" }, - { "text": "DIP Switch", "link": "/feature_dip_switch" }, - { "text": "Encoders", "link": "/feature_encoders" }, - { "text": "Haptic Feedback", "link": "/feature_haptic_feedback" }, - { "text": "Joystick", "link": "/feature_joystick" }, - { "text": "LED Indicators", "link": "/feature_led_indicators" }, - { "text": "MIDI", "link": "/feature_midi" }, - { "text": "Pointing Device", "link": "/feature_pointing_device" }, - { "text": "PS/2 Mouse", "link": "/feature_ps2_mouse" }, - { "text": "Split Keyboard", "link": "/feature_split_keyboard" }, - { "text": "Stenography", "link": "/feature_stenography" } + { "text": "DIP Switch", "link": "/features/dip_switch" }, + { "text": "Encoders", "link": "/features/encoders" }, + { "text": "Haptic Feedback", "link": "/features/haptic_feedback" }, + { "text": "Joystick", "link": "/features/joystick" }, + { "text": "LED Indicators", "link": "/features/led_indicators" }, + { "text": "MIDI", "link": "/features/midi" }, + { "text": "Pointing Device", "link": "/features/pointing_device" }, + { "text": "PS/2 Mouse", "link": "/features/ps2_mouse" }, + { "text": "Split Keyboard", "link": "/features/split_keyboard" }, + { "text": "Stenography", "link": "/features/stenography" } ] }, { @@ -226,19 +226,19 @@ "text": "Drivers", "link": "hardware_drivers", "items": [ - { "text": "ADC Driver", "link": "/adc_driver" }, - { "text": "APA102 Driver", "link": "/apa102_driver" }, - { "text": "Audio Driver", "link": "/audio_driver" }, - { "text": "I2C Driver", "link": "/i2c_driver" }, - { "text": "SPI Driver", "link": "/spi_driver" }, - { "text": "WS2812 Driver", "link": "/ws2812_driver" }, - { "text": "EEPROM Driver", "link": "/eeprom_driver" }, - { "text": "Flash Driver", "link": "/flash_driver" }, - { "text": "'serial' Driver", "link": "/serial_driver" }, - { "text": "UART Driver", "link": "/uart_driver" } + { "text": "ADC Driver", "link": "/drivers/adc" }, + { "text": "APA102 Driver", "link": "/drivers/apa102" }, + { "text": "Audio Driver", "link": "/drivers/audio" }, + { "text": "EEPROM Driver", "link": "/drivers/eeprom" }, + { "text": "Flash Driver", "link": "/drivers/flash" }, + { "text": "I2C Driver", "link": "/drivers/i2c" }, + { "text": "'serial' Driver", "link": "/drivers/serial" }, + { "text": "SPI Driver", "link": "/drivers/spi" }, + { "text": "UART Driver", "link": "/drivers/uart" }, + { "text": "WS2812 Driver", "link": "/drivers/ws2812" } ] }, - { "text": "GPIO Controls", "link": "/gpio_control" }, + { "text": "GPIO Controls", "link": "/drivers/gpio" }, { "text": "Keyboard Guidelines", "link": "/hardware_keyboard_guidelines" } ] }, diff --git a/docs/adc_driver.md b/docs/adc_driver.md deleted file mode 100644 index a1ab5a5251..0000000000 --- a/docs/adc_driver.md +++ /dev/null @@ -1,173 +0,0 @@ -# ADC Driver - -QMK can leverage the Analog-to-Digital Converter (ADC) on supported MCUs to measure voltages on certain pins. This can be useful for implementing things such as battery level indicators for Bluetooth keyboards, or volume controls using a potentiometer, as opposed to a [rotary encoder](feature_encoders). - -This driver currently supports both AVR and a limited selection of ARM devices. The values returned are 10-bit integers (0-1023) mapped between 0V and VCC (usually 5V or 3.3V for AVR, 3.3V only for ARM), however on ARM there is more flexibility in control of operation through `#define`s if you need more precision. - -## Usage - -To use this driver, add the following to your `rules.mk`: - -```make -ANALOG_DRIVER_REQUIRED = yes -``` - -Then place this include at the top of your code: - -```c -#include "analog.h" -``` - -## Channels - -### AVR - -|Channel|AT90USB64/128|ATmega16/32U4|ATmega32A|ATmega328/P| -|-------|-------------|-------------|---------|----------| -|0 |`F0` |`F0` |`A0` |`C0` | -|1 |`F1` |`F1` |`A1` |`C1` | -|2 |`F2` | |`A2` |`C2` | -|3 |`F3` | |`A3` |`C3` | -|4 |`F4` |`F4` |`A4` |`C4` | -|5 |`F5` |`F5` |`A5` |`C5` | -|6 |`F6` |`F6` |`A6` |* | -|7 |`F7` |`F7` |`A7` |* | -|8 | |`D4` | | | -|9 | |`D6` | | | -|10 | |`D7` | | | -|11 | |`B4` | | | -|12 | |`B5` | | | -|13 | |`B6` | | | - -\* The ATmega328/P possesses two extra ADC channels; however, they are not present on the DIP pinout, and are not shared with GPIO pins. You can use `adc_read()` directly to gain access to these. - -### ARM - -#### STM32 - -Note that some of these pins are doubled-up on ADCs with the same channel. This is because the pins can be used for either ADC. - -Also note that the F0 and F3 use different numbering schemes. The F0 has a single ADC and the channels are 0-indexed, whereas the F3 has 4 ADCs and the channels are 1-indexed. This is because the F0 uses the `ADCv1` implementation of the ADC, whereas the F3 uses the `ADCv3` implementation. - -|ADC|Channel|STM32F0xx|STM32F1xx|STM32F3xx|STM32F4xx| -|---|-------|---------|---------|---------|---------| -|1 |0 |`A0` |`A0` | |`A0` | -|1 |1 |`A1` |`A1` |`A0` |`A1` | -|1 |2 |`A2` |`A2` |`A1` |`A2` | -|1 |3 |`A3` |`A3` |`A2` |`A3` | -|1 |4 |`A4` |`A4` |`A3` |`A4` | -|1 |5 |`A5` |`A5` |`F4` |`A5` | -|1 |6 |`A6` |`A6` |`C0` |`A6` | -|1 |7 |`A7` |`A7` |`C1` |`A7` | -|1 |8 |`B0` |`B0` |`C2` |`B0` | -|1 |9 |`B1` |`B1` |`C3` |`B1` | -|1 |10 |`C0` |`C0` |`F2` |`C0` | -|1 |11 |`C1` |`C1` | |`C1` | -|1 |12 |`C2` |`C2` | |`C2` | -|1 |13 |`C3` |`C3` | |`C3` | -|1 |14 |`C4` |`C4` | |`C4` | -|1 |15 |`C5` |`C5` | |`C5` | -|1 |16 | | | | | -|2 |0 | |`A0`¹ | |`A0`² | -|2 |1 | |`A1`¹ |`A4` |`A1`² | -|2 |2 | |`A2`¹ |`A5` |`A2`² | -|2 |3 | |`A3`¹ |`A6` |`A3`² | -|2 |4 | |`A4`¹ |`A7` |`A4`² | -|2 |5 | |`A5`¹ |`C4` |`A5`² | -|2 |6 | |`A6`¹ |`C0` |`A6`² | -|2 |7 | |`A7`¹ |`C1` |`A7`² | -|2 |8 | |`B0`¹ |`C2` |`B0`² | -|2 |9 | |`B1`¹ |`C3` |`B1`² | -|2 |10 | |`C0`¹ |`F2` |`C0`² | -|2 |11 | |`C1`¹ |`C5` |`C1`² | -|2 |12 | |`C2`¹ |`B2` |`C2`² | -|2 |13 | |`C3`¹ | |`C3`² | -|2 |14 | |`C4`¹ | |`C4`² | -|2 |15 | |`C5`¹ | |`C5`² | -|2 |16 | | | | | -|3 |0 | |`A0`¹ | |`A0`² | -|3 |1 | |`A1`¹ |`B1` |`A1`² | -|3 |2 | |`A2`¹ |`E9` |`A2`² | -|3 |3 | |`A3`¹ |`E13` |`A3`² | -|3 |4 | |`F6`¹ | |`F6`² | -|3 |5 | |`F7`¹ |`B13` |`F7`² | -|3 |6 | |`F8`¹ |`E8` |`F8`² | -|3 |7 | |`F9`¹ |`D10` |`F9`² | -|3 |8 | |`F10`¹ |`D11` |`F10`² | -|3 |9 | | |`D12` |`F3`² | -|3 |10 | |`C0`¹ |`D13` |`C0`² | -|3 |11 | |`C1`¹ |`D14` |`C1`² | -|3 |12 | |`C2`¹ |`B0` |`C2`² | -|3 |13 | |`C3`¹ |`E7` |`C3`² | -|3 |14 | | |`E10` |`F4`² | -|3 |15 | | |`E11` |`F5`² | -|3 |16 | | |`E12` | | -|4 |1 | | |`E14` | | -|4 |2 | | |`E15` | | -|4 |3 | | |`B12` | | -|4 |4 | | |`B14` | | -|4 |5 | | |`B15` | | -|4 |6 | | |`E8` | | -|4 |7 | | |`D10` | | -|4 |8 | | |`D11` | | -|4 |9 | | |`D12` | | -|4 |10 | | |`D13` | | -|4 |11 | | |`D14` | | -|4 |12 | | |`D8` | | -|4 |13 | | |`D9` | | -|4 |14 | | | | | -|4 |15 | | | | | -|4 |16 | | | | | - -¹ As of ChibiOS 20.3.4, the ADC driver for STM32F1xx devices supports only ADC1, therefore any configurations involving ADC2 or ADC3 cannot actually be used. In particular, pins `F6`…`F10`, which are present at least on some STM32F103x[C-G] devices, cannot be used as ADC inputs because of this driver limitation. - -² Not all STM32F4xx devices have ADC2 and/or ADC3, therefore some configurations shown in this table may be unavailable; in particular, pins `F4`…`F10` cannot be used as ADC inputs on devices which do not have ADC3. Check the device datasheet to confirm which pin functions are supported. - -#### RP2040 - -RP2040 has only a single ADC (`ADCD1` in ChibiOS); in the QMK API the index for that ADC is 0. - -|Channel|Pin | -|-------|-------------------| -|0 |`GP26` | -|1 |`GP27` | -|2 |`GP28` | -|3 |`GP29` | -|4 |Temperature sensor*| - - -* The temperature sensor is disabled by default and needs to be enabled by the RP2040-specific function: `adcRPEnableTS(&ADCD1)`. The ADC must be initialized before calling that function; an easy way to ensure that is to perform a dummy conversion. - -## Functions - -### AVR - -|Function |Description | -|----------------------------|-------------------------------------------------------------------------------------------------------------------| -|`analogReference(mode)` |Sets the analog voltage reference source. Must be one of `ADC_REF_EXTERNAL`, `ADC_REF_POWER` or `ADC_REF_INTERNAL`.| -|`analogReadPin(pin)` |Reads the value from the specified pin, eg. `F6` for ADC6 on the ATmega32U4. | -|`pinToMux(pin)` |Translates a given pin to a mux value. If an unsupported pin is given, returns the mux value for "0V (GND)". | -|`adc_read(mux)` |Reads the value from the ADC according to the specified mux. See your MCU's datasheet for more information. | - -### ARM - -|Function |Description | -|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -|`analogReadPin(pin)` |Reads the value from the specified pin, eg. `A0` for channel 0 on the STM32F0 and ADC1 channel 1 on the STM32F3. Note that if a pin can be used for multiple ADCs, it will pick the lower numbered ADC for this function. eg. `C0` will be channel 6 of ADC 1 when it could be used for ADC 2 as well.| -|`analogReadPinAdc(pin, adc)`|Reads the value from the specified pin and ADC, eg. `C0, 1` will read from channel 6, ADC 2 instead of ADC 1. Note that the ADCs are 0-indexed for this function. | -|`pinToMux(pin)` |Translates a given pin to a channel and ADC combination. If an unsupported pin is given, returns the mux value for "0V (GND)". | -|`adc_read(mux)` |Reads the value from the ADC according to the specified pin and ADC combination. See your MCU's datasheet for more information. | - -## Configuration - -## ARM - -The ARM implementation of the ADC has a few additional options that you can override in your own keyboards and keymaps to change how it operates. Please consult the corresponding `hal_adc_lld.h` in ChibiOS for your specific microcontroller for further documentation on your available options. - -|`#define` |Type |Default |Description | -|---------------------|------|----------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -|`ADC_CIRCULAR_BUFFER`|`bool`|`false` |If `true`, then the implementation will use a circular buffer. | -|`ADC_NUM_CHANNELS` |`int` |`1` |Sets the number of channels that will be scanned as part of an ADC operation. The current implementation only supports `1`. | -|`ADC_BUFFER_DEPTH` |`int` |`2` |Sets the depth of each result. Since we are only getting a 10-bit result by default, we set this to 2 bytes so we can contain our one value. This could be set to 1 if you opt for an 8-bit or lower result.| -|`ADC_SAMPLING_RATE` |`int` |`ADC_SMPR_SMP_1P5` |Sets the sampling rate of the ADC. By default, it is set to the fastest setting. | -|`ADC_RESOLUTION` |`int` |`ADC_CFGR1_RES_10BIT` or `ADC_CFGR_RES_10BITS`|The resolution of your result. We choose 10 bit by default, but you can opt for 12, 10, 8, or 6 bit. Different MCUs use slightly different names for the resolution constants. | diff --git a/docs/apa102_driver.md b/docs/apa102_driver.md deleted file mode 100644 index 0f905e3f18..0000000000 --- a/docs/apa102_driver.md +++ /dev/null @@ -1,49 +0,0 @@ -# APA102 Driver {#apa102-driver} - -This driver provides support for APA102 addressable RGB LEDs. They are similar to the [WS2812](ws2812_driver) LEDs, but have increased data and refresh rates. - -## Usage {#usage} - -In most cases, the APA102 driver code is automatically included if you are using either the [RGBLight](feature_rgblight) or [RGB Matrix](feature_rgb_matrix) feature with the `apa102` driver set, and you would use those APIs instead. - -However, if you need to use the driver standalone, add the following to your `rules.mk`: - -```make -APA102_DRIVER_REQUIRED = yes -``` - -You can then call the APA102 API by including `apa102.h` in your code. - -## Basic Configuration {#basic-configuration} - -Add the following to your `config.h`: - -|Define |Default |Description | -|---------------------------|-------------|------------------------------------------------------------------| -|`APA102_DI_PIN` |*Not defined*|The GPIO pin connected to the DI pin of the first LED in the chain| -|`APA102_CI_PIN` |*Not defined*|The GPIO pin connected to the CI pin of the first LED in the chain| -|`APA102_DEFAULT_BRIGHTNESS`|`31` |The default global brightness level of the LEDs, from 0 to 31 | - -## API {#api} - -### `void apa102_setleds(rgb_led_t *start_led, uint16_t num_leds)` - -Send RGB data to the APA102 LED chain. - -#### Arguments {#api-apa102-setleds-arguments} - - - `rgb_led_t *start_led` - A pointer to the LED array. - - `uint16_t num_leds` - The length of the LED array. - ---- - -### `void apa102_set_brightness(uint8_t brightness)` - -Set the global brightness. - -#### Arguments {#api-apa102-set-brightness-arguments} - - - `uint8_t brightness` - The brightness level to set, from 0 to 31. diff --git a/docs/audio_driver.md b/docs/audio_driver.md deleted file mode 100644 index 4a71b4f411..0000000000 --- a/docs/audio_driver.md +++ /dev/null @@ -1,239 +0,0 @@ -# Audio Driver {#audio-driver} - -The [Audio feature](feature_audio) breaks the hardware specifics out into separate, exchangeable driver units, with a common interface to the audio-"core" - which itself handles playing songs and notes while tracking their progress in an internal state, initializing/starting/stopping the driver as needed. - -Not all MCUs support every available driver, either the platform-support is not there (yet?) or the MCU simply does not have the required hardware peripheral. - - -## AVR {#avr} - -Boards built around an Atmega32U4 can use two sets of PWM capable pins, each driving a separate speaker. -The possible configurations are: - -| | Timer3 | Timer1 | -|--------------|-------------|--------------| -| one speaker | C4,C5 or C6 | | -| one speaker | | B4, B5 or B7 | -| two speakers | C4,C5 or C6 | B4, B5 or B7 | - -Currently there is only one/default driver for AVR based boards, which is automatically configured to: - -```make -AUDIO_DRIVER = pwm_hardware -``` - - -## ARM {#arm} - -For Arm based boards, QMK depends on ChibiOS - hence any MCU supported by the later is likely usable, as long as certain hardware peripherals are available. - -Supported wiring configurations, with their ChibiOS/MCU peripheral requirement are listed below; -piezo speakers are marked with :one: for the first/primary and :two: for the secondary. - - | driver | GPTD6
Tim6 | GPTD7
Tim7 | GPTD8
Tim8 | PWMD11
Tim1_Ch1 | - |--------------|------------------------------------------|------------------------|---------------|-------------------------------| - | dac_basic | A4+DACD1 = :one: | A5+DACD2 = :one: | state | | - | | A4+DACD1 = :one: + Gnd | A5+DACD2 = :two: + Gnd | state | | - | | A4+DACD1 = :two: + Gnd | A5+DACD2 = :one: + Gnd | state | | - | | A4+DACD1 = :one: + Gnd | | state | | - | | | A5+DACD2 = :one: + Gnd | state | | - | dac_additive | A4+DACD1 = :one: + Gnd | | | | - | | A5+DACD2 = :one: + Gnd | | | | - | | A4+DACD1 + A5+DACD2 = :one: 2 | | | | - | pwm_software | state-update | | | any = :one: | - | pwm hardware | state-update | | | A8 = :one: 3 | - - -1: the routing and alternate functions for PWM differ sometimes between STM32 MCUs, if in doubt consult the data-sheet -2: one piezo connected to A4 and A5, with AUDIO_PIN_ALT_AS_NEGATIVE set -3: TIM1_CH1 = A8 on STM32F103C8, other combinations are possible, see Data-sheet. configured with: AUDIO_PWM_DRIVER and AUDIO_PWM_CHANNEL - - - -### DAC basic {#dac-basic} - -The default driver for ARM boards, in absence of an overriding configuration. -This driver needs one Timer per enabled/used DAC channel, to trigger conversion; and a third timer to trigger state updates with the audio-core. - -Additionally, in the board config, you'll want to make changes to enable the DACs, GPT for Timers 6, 7 and 8: - -```c -//halconf.h: -#define HAL_USE_DAC TRUE -#define HAL_USE_GPT TRUE -#include_next -``` - -```c -// mcuconf.h: -#include_next -#undef STM32_DAC_USE_DAC1_CH1 -#define STM32_DAC_USE_DAC1_CH1 TRUE -#undef STM32_DAC_USE_DAC1_CH2 -#define STM32_DAC_USE_DAC1_CH2 TRUE -#undef STM32_GPT_USE_TIM6 -#define STM32_GPT_USE_TIM6 TRUE -#undef STM32_GPT_USE_TIM7 -#define STM32_GPT_USE_TIM7 TRUE -#undef STM32_GPT_USE_TIM8 -#define STM32_GPT_USE_TIM8 TRUE -``` - -::: tip -Note: DAC1 (A4) uses TIM6, DAC2 (A5) uses TIM7, and the audio state timer uses TIM8 (configurable). -::: - -You can also change the timer used for the overall audio state by defining the driver. For instance: - -```c -#define AUDIO_STATE_TIMER GPTD9 -``` - -### DAC additive {#dac-additive} - -only needs one timer (GPTD6, Tim6) to trigger the DAC unit to do a conversion; the audio state updates are in turn triggered during the DAC callback. - -Additionally, in the board config, you'll want to make changes to enable the DACs, GPT for Timer 6: - -```c -//halconf.h: -#define HAL_USE_DAC TRUE -#define HAL_USE_GPT TRUE -#include_next -``` - -```c -// mcuconf.h: -#include_next -#undef STM32_DAC_USE_DAC1_CH1 -#define STM32_DAC_USE_DAC1_CH1 TRUE -#undef STM32_DAC_USE_DAC1_CH2 -#define STM32_DAC_USE_DAC1_CH2 TRUE -#undef STM32_GPT_USE_TIM6 -#define STM32_GPT_USE_TIM6 TRUE -``` - -### DAC Config - -| Define | Defaults | Description | -| -------------------------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `AUDIO_DAC_SAMPLE_MAX` | `4095U` | Highest value allowed. Lower value means lower volume. And 4095U is the upper limit, since this is limited to a 12 bit value. Only effects non-pregenerated samples. | -| `AUDIO_DAC_OFF_VALUE` | `AUDIO_DAC_SAMPLE_MAX / 2` | The value of the DAC when not playing anything. Some setups may require a high (`AUDIO_DAC_SAMPLE_MAX`) or low (`0`) value here. | -| `AUDIO_MAX_SIMULTANEOUS_TONES` | __see next table__ | The number of tones that can be played simultaneously. A value that is too high may freeze the controller or glitch out when too many tones are being played. | -| `AUDIO_DAC_SAMPLE_RATE` | __see next table__ | Effective bit rate of the DAC (in hertz), higher limits simultaneous tones, and lower sacrifices quality. | -| `AUDIO_DAC_BUFFER_SIZE` | __see next table__ | Number of samples generated every refill. Too few may cause excessive CPU load; too many may cause freezes, RAM or flash exhaustion or lags during matrix scanning. | - -There are a number of predefined quality settings that you can use, with "sane minimum" being the default. You can use custom values by simply defining the sample rate, number of simultaneous tones and buffer size, instead of using one of the listed presets. - -| Define | Sample Rate | Simultaneous tones | Buffer size | -| --------------------------------- | ----------- | ------------------- | ----------- | -| `AUDIO_DAC_QUALITY_VERY_LOW` | `11025U` | `8` | `64U` | -| `AUDIO_DAC_QUALITY_LOW` | `22050U` | `4` | `128U` | -| `AUDIO_DAC_QUALITY_HIGH` | `44100U` | `2` | `256U` | -| `AUDIO_DAC_QUALITY_VERY_HIGH` | `88200U` | `1` | `256U` | -| `AUDIO_DAC_QUALITY_SANE_MINIMUM` | `16384U` | `8` | `64U` | - -#### Notes on buffer size {#buffer-size} - -By default, the buffer size attempts to keep to these constraints: - -* The interval between buffer refills can't be too short, since the microcontroller would then only be servicing buffer refills and would freeze up. -* On the additive driver, the interval between buffer refills can't be too long, since matrix scanning would suffer lengthy pauses every so often, which would delay key presses or releases or lose some short taps altogether. -* The interval between buffer refills is kept to a minimum, which allows notes to stop as soon as possible after they should. -* For greater compatibility, the buffer size should be a power of 2. -* The buffer size being too large causes resource exhaustion leading to build failures or freezing at runtime: RAM usage (on the additive driver) or flash usage (on the basic driver). - -You can lower the buffer size if you need a bit more space in your firmware, or raise it if your keyboard freezes up. - - -```c - /* zero crossing (or approach, whereas zero == DAC_OFF_VALUE, which can be configured to anything from 0 to DAC_SAMPLE_MAX) - * ============================*=*========================== AUDIO_DAC_SAMPLE_MAX - * * * - * * * - * --------------------------------------------------------- - * * * } AUDIO_DAC_SAMPLE_MAX/100 - * --------------------------------------------------------- AUDIO_DAC_OFF_VALUE - * * * } AUDIO_DAC_SAMPLE_MAX/100 - * --------------------------------------------------------- - * * - * * * - * * * - * =====*=*================================================= 0x0 - */ -``` - - -### PWM hardware {#pwm-hardware} - -This driver uses the ChibiOS-PWM system to produce a square-wave on specific output pins that are connected to the PWM hardware. -The hardware directly toggles the pin via its alternate function. See your MCU's data-sheet for which pin can be driven by what timer - looking for TIMx_CHy and the corresponding alternate function. - -A configuration example for the STM32F103C8 would be: -```c -//halconf.h: -#define HAL_USE_PWM TRUE -#define HAL_USE_PAL TRUE -#include_next -``` - -```c -// mcuconf.h: -#include_next -#undef STM32_PWM_USE_TIM1 -#define STM32_PWM_USE_TIM1 TRUE -``` - -If we now target pin A8, looking through the data-sheet of the STM32F103C8, for the timers and alternate functions -- TIM1_CH1 = PA8 <- alternate0 -- TIM1_CH2 = PA9 -- TIM1_CH3 = PA10 -- TIM1_CH4 = PA11 - -with all this information, the configuration would contain these lines: -```c -//config.h: -#define AUDIO_PIN A8 -#define AUDIO_PWM_DRIVER PWMD1 -#define AUDIO_PWM_CHANNEL 1 -``` - -ChibiOS uses GPIOv1 for the F103, which only knows of one alternate function. -On 'larger' STM32s, GPIOv2 or GPIOv3 are used; with them it is also necessary to configure `AUDIO_PWM_PAL_MODE` to the correct alternate function for the selected pin, timer and timer-channel. - -You can also use the Complementary output (`TIMx_CHyN`) for PWM on supported controllers. To enable this functionality, you will need to make the following changes: -```c -// config.h: -#define AUDIO_PWM_COMPLEMENTARY_OUTPUT -``` - -### PWM software {#pwm-software} - -This driver uses the PWM callbacks from PWMD1 with TIM1_CH1 to toggle the selected AUDIO_PIN in software. -During the same callback, with AUDIO_PIN_ALT_AS_NEGATIVE set, the AUDIO_PIN_ALT is toggled inversely to AUDIO_PIN. This is useful for setups that drive a piezo from two pins (instead of one and Gnd). - -You can also change the timer used for software PWM by defining the driver. For instance: - -```c -#define AUDIO_STATE_TIMER GPTD8 -``` - - -### Testing Notes {#testing-notes} - -While not an exhaustive list, the following table provides the scenarios that have been partially validated: - -| | DAC basic | DAC additive | PWM hardware | PWM software | -| ------------------------ | ------------------ | ------------------ | ------------------ | ------------------ | -| Atmega32U4 | :o: | :o: | :heavy_check_mark: | :o: | -| RP2040 | :x: | :x: | :heavy_check_mark: | ? | -| STM32F103C8 (bluepill) | :x: | :x: | :heavy_check_mark: | :heavy_check_mark: | -| STM32F303CCT6 (proton-c) | :heavy_check_mark: | :heavy_check_mark: | ? | :heavy_check_mark: | -| STM32F405VG | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | -| L0xx | :x: (no Tim8) | ? | ? | ? | - -:heavy_check_mark: : works and was tested -:o: : does not apply -:x: : not supported by MCU - -*Other supported ChibiOS boards and/or pins may function, it will be highly chip and configuration dependent.* diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 5a85356e70..7d74d8e617 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -749,7 +749,7 @@ options: ## `qmk generate-rgb-breathe-table` -This command generates a lookup table (LUT) header file for the [RGB Lighting](feature_rgblight) 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/`. +This command generates a lookup table (LUT) header file for the [RGB Lighting](features/rgblight) 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/config_options.md b/docs/config_options.md index 236649a0ea..a1ca8c8d50 100644 --- a/docs/config_options.md +++ b/docs/config_options.md @@ -207,7 +207,7 @@ If you define these options you will enable the associated feature, which may in * `#define TAP_HOLD_CAPS_DELAY 80` * Sets the delay for Tap Hold keys (`LT`, `MT`) when using `KC_CAPS_LOCK` 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). + * Sets the key repeat interval for [key overrides](features/key_overrides). * `#define LEGACY_MAGIC_HANDLING` * Enables magic configuration handling for advanced keycodes (such as Mod Tap and Layer Tap) @@ -217,14 +217,14 @@ If you define these options you will enable the associated feature, which may in * `#define WS2812_DI_PIN D7` * pin the DI on the WS2812 is hooked-up to * `#define RGBLIGHT_LAYERS` - * Lets you define [lighting layers](feature_rgblight#lighting-layers) that can be toggled on or off. Great for showing the current keyboard layer or caps lock state. + * Lets you define [lighting layers](features/rgblight#lighting-layers) that can be toggled on or off. Great for showing the current keyboard layer or caps lock state. * `#define RGBLIGHT_MAX_LAYERS` - * Defaults to 8. Can be expanded up to 32 if more [lighting layers](feature_rgblight#lighting-layers) are needed. + * Defaults to 8. Can be expanded up to 32 if more [lighting layers](features/rgblight#lighting-layers) are needed. * Note: Increasing the maximum will increase the firmware size and slow sync on split keyboards. * `#define RGBLIGHT_LAYER_BLINK` - * Adds ability to [blink](feature_rgblight#lighting-layer-blink) a lighting layer for a specified number of milliseconds (e.g. to acknowledge an action). + * Adds ability to [blink](features/rgblight#lighting-layer-blink) a lighting layer for a specified number of milliseconds (e.g. to acknowledge an action). * `#define RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF` - * If defined, then [lighting layers](feature_rgblight#overriding-rgb-lighting-onoff-status) will be shown even if RGB Light is off. + * If defined, then [lighting layers](features/rgblight#overriding-rgb-lighting-onoff-status) will be shown even if RGB Light is off. * `#define RGBLIGHT_LED_COUNT 12` * number of LEDs * `#define RGBLIGHT_SPLIT` @@ -358,7 +358,7 @@ There are a few different ways to set handedness for split keyboards (listed in * `#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#custom-data-sync) for more information. + * Allows for custom data sync with the slave when using the QMK-provided split transport. See [custom data sync between sides](features/split_keyboard#custom-data-sync) for more information. # The `rules.mk` File diff --git a/docs/custom_quantum_functions.md b/docs/custom_quantum_functions.md index ac21f0e039..729f0cd028 100644 --- a/docs/custom_quantum_functions.md +++ b/docs/custom_quantum_functions.md @@ -206,7 +206,7 @@ Similar to `matrix_scan_*`, these are called as often as the MCU can handle. To ### Example `void housekeeping_task_user(void)` implementation -This example will show you how to use `void housekeeping_task_user(void)` to turn off [RGB Light](feature_rgblight). For RGB Matrix, the [builtin](feature_rgb_matrix#additional-configh-options) `RGB_MATRIX_TIMEOUT` should be used. +This example will show you how to use `void housekeeping_task_user(void)` to turn off [RGB Light](features/rgblight). For RGB Matrix, the [builtin](features/rgb_matrix#additional-configh-options) `RGB_MATRIX_TIMEOUT` should be used. First, add the following lines to your keymap's `config.h`: diff --git a/docs/documentation_best_practices.md b/docs/documentation_best_practices.md index d41ec28f19..bc64472e24 100644 --- a/docs/documentation_best_practices.md +++ b/docs/documentation_best_practices.md @@ -69,4 +69,4 @@ This page describes my cool feature. You can use my cool feature to make coffee |KC_SUGAR||Order Sugar| ``` -Place your documentation into `docs/feature_.md`, and add that file to the appropriate place in `docs/_sidebar.json`. If you have added any keycodes be sure to add them to `docs/keycodes.md` with a link back to your feature page. +Place your documentation into `docs/features/.md`, and add that file to the appropriate place in `docs/_sidebar.json`. If you have added any keycodes be sure to add them to `docs/keycodes.md` with a link back to your feature page. diff --git a/docs/driver_installation_zadig.md b/docs/driver_installation_zadig.md index ce16ada166..099376faeb 100644 --- a/docs/driver_installation_zadig.md +++ b/docs/driver_installation_zadig.md @@ -8,8 +8,8 @@ We recommend the use of the [Zadig](https://zadig.akeo.ie/) utility. If you have ## Installation -Put your keyboard into bootloader mode, either by hitting the `QK_BOOT` keycode (which may be on a different layer), or by pressing the reset switch that's usually located on the underside of the board. If your keyboard has neither, try holding Escape or Space+`B` as you plug it in (see the [Bootmagic Lite](feature_bootmagic) docs for more details). Some boards use [Command](feature_command) instead of Bootmagic; in this case, you can enter bootloader mode by hitting Left Shift+Right Shift+`B` or Left Shift+Right Shift+Escape at any point while the keyboard is plugged in. -Some keyboards may have specific instructions for entering the bootloader. For example, the [Bootmagic Lite](feature_bootmagic) key (default: Escape) might be on a different key, e.g. Left Control; or the magic combination for Command (default: Left Shift+Right Shift) might require you to hold something else, e.g. Left Control+Right Control. Refer to the board's README file if you are unsure. +Put your keyboard into bootloader mode, either by hitting the `QK_BOOT` keycode (which may be on a different layer), or by pressing the reset switch that's usually located on the underside of the board. If your keyboard has neither, try holding Escape or Space+`B` as you plug it in (see the [Bootmagic Lite](features/bootmagic) docs for more details). Some boards use [Command](features/command) instead of Bootmagic; in this case, you can enter bootloader mode by hitting Left Shift+Right Shift+`B` or Left Shift+Right Shift+Escape at any point while the keyboard is plugged in. +Some keyboards may have specific instructions for entering the bootloader. For example, the [Bootmagic Lite](features/bootmagic) key (default: Escape) might be on a different key, e.g. Left Control; or the magic combination for Command (default: Left Shift+Right Shift) might require you to hold something else, e.g. Left Control+Right Control. Refer to the board's README file if you are unsure. To put a device in bootloader mode with USBaspLoader, tap the `RESET` button while holding down the `BOOT` button. Alternatively, hold `BOOT` while inserting the USB cable. diff --git a/docs/drivers/adc.md b/docs/drivers/adc.md new file mode 100644 index 0000000000..d89068c2ae --- /dev/null +++ b/docs/drivers/adc.md @@ -0,0 +1,173 @@ +# ADC Driver + +QMK can leverage the Analog-to-Digital Converter (ADC) on supported MCUs to measure voltages on certain pins. This can be useful for implementing things such as battery level indicators for Bluetooth keyboards, or volume controls using a potentiometer, as opposed to a [rotary encoder](../features/encoders). + +This driver currently supports both AVR and a limited selection of ARM devices. The values returned are 10-bit integers (0-1023) mapped between 0V and VCC (usually 5V or 3.3V for AVR, 3.3V only for ARM), however on ARM there is more flexibility in control of operation through `#define`s if you need more precision. + +## Usage + +To use this driver, add the following to your `rules.mk`: + +```make +ANALOG_DRIVER_REQUIRED = yes +``` + +Then place this include at the top of your code: + +```c +#include "analog.h" +``` + +## Channels + +### AVR + +|Channel|AT90USB64/128|ATmega16/32U4|ATmega32A|ATmega328/P| +|-------|-------------|-------------|---------|----------| +|0 |`F0` |`F0` |`A0` |`C0` | +|1 |`F1` |`F1` |`A1` |`C1` | +|2 |`F2` | |`A2` |`C2` | +|3 |`F3` | |`A3` |`C3` | +|4 |`F4` |`F4` |`A4` |`C4` | +|5 |`F5` |`F5` |`A5` |`C5` | +|6 |`F6` |`F6` |`A6` |* | +|7 |`F7` |`F7` |`A7` |* | +|8 | |`D4` | | | +|9 | |`D6` | | | +|10 | |`D7` | | | +|11 | |`B4` | | | +|12 | |`B5` | | | +|13 | |`B6` | | | + +\* The ATmega328/P possesses two extra ADC channels; however, they are not present on the DIP pinout, and are not shared with GPIO pins. You can use `adc_read()` directly to gain access to these. + +### ARM + +#### STM32 + +Note that some of these pins are doubled-up on ADCs with the same channel. This is because the pins can be used for either ADC. + +Also note that the F0 and F3 use different numbering schemes. The F0 has a single ADC and the channels are 0-indexed, whereas the F3 has 4 ADCs and the channels are 1-indexed. This is because the F0 uses the `ADCv1` implementation of the ADC, whereas the F3 uses the `ADCv3` implementation. + +|ADC|Channel|STM32F0xx|STM32F1xx|STM32F3xx|STM32F4xx| +|---|-------|---------|---------|---------|---------| +|1 |0 |`A0` |`A0` | |`A0` | +|1 |1 |`A1` |`A1` |`A0` |`A1` | +|1 |2 |`A2` |`A2` |`A1` |`A2` | +|1 |3 |`A3` |`A3` |`A2` |`A3` | +|1 |4 |`A4` |`A4` |`A3` |`A4` | +|1 |5 |`A5` |`A5` |`F4` |`A5` | +|1 |6 |`A6` |`A6` |`C0` |`A6` | +|1 |7 |`A7` |`A7` |`C1` |`A7` | +|1 |8 |`B0` |`B0` |`C2` |`B0` | +|1 |9 |`B1` |`B1` |`C3` |`B1` | +|1 |10 |`C0` |`C0` |`F2` |`C0` | +|1 |11 |`C1` |`C1` | |`C1` | +|1 |12 |`C2` |`C2` | |`C2` | +|1 |13 |`C3` |`C3` | |`C3` | +|1 |14 |`C4` |`C4` | |`C4` | +|1 |15 |`C5` |`C5` | |`C5` | +|1 |16 | | | | | +|2 |0 | |`A0`¹ | |`A0`² | +|2 |1 | |`A1`¹ |`A4` |`A1`² | +|2 |2 | |`A2`¹ |`A5` |`A2`² | +|2 |3 | |`A3`¹ |`A6` |`A3`² | +|2 |4 | |`A4`¹ |`A7` |`A4`² | +|2 |5 | |`A5`¹ |`C4` |`A5`² | +|2 |6 | |`A6`¹ |`C0` |`A6`² | +|2 |7 | |`A7`¹ |`C1` |`A7`² | +|2 |8 | |`B0`¹ |`C2` |`B0`² | +|2 |9 | |`B1`¹ |`C3` |`B1`² | +|2 |10 | |`C0`¹ |`F2` |`C0`² | +|2 |11 | |`C1`¹ |`C5` |`C1`² | +|2 |12 | |`C2`¹ |`B2` |`C2`² | +|2 |13 | |`C3`¹ | |`C3`² | +|2 |14 | |`C4`¹ | |`C4`² | +|2 |15 | |`C5`¹ | |`C5`² | +|2 |16 | | | | | +|3 |0 | |`A0`¹ | |`A0`² | +|3 |1 | |`A1`¹ |`B1` |`A1`² | +|3 |2 | |`A2`¹ |`E9` |`A2`² | +|3 |3 | |`A3`¹ |`E13` |`A3`² | +|3 |4 | |`F6`¹ | |`F6`² | +|3 |5 | |`F7`¹ |`B13` |`F7`² | +|3 |6 | |`F8`¹ |`E8` |`F8`² | +|3 |7 | |`F9`¹ |`D10` |`F9`² | +|3 |8 | |`F10`¹ |`D11` |`F10`² | +|3 |9 | | |`D12` |`F3`² | +|3 |10 | |`C0`¹ |`D13` |`C0`² | +|3 |11 | |`C1`¹ |`D14` |`C1`² | +|3 |12 | |`C2`¹ |`B0` |`C2`² | +|3 |13 | |`C3`¹ |`E7` |`C3`² | +|3 |14 | | |`E10` |`F4`² | +|3 |15 | | |`E11` |`F5`² | +|3 |16 | | |`E12` | | +|4 |1 | | |`E14` | | +|4 |2 | | |`E15` | | +|4 |3 | | |`B12` | | +|4 |4 | | |`B14` | | +|4 |5 | | |`B15` | | +|4 |6 | | |`E8` | | +|4 |7 | | |`D10` | | +|4 |8 | | |`D11` | | +|4 |9 | | |`D12` | | +|4 |10 | | |`D13` | | +|4 |11 | | |`D14` | | +|4 |12 | | |`D8` | | +|4 |13 | | |`D9` | | +|4 |14 | | | | | +|4 |15 | | | | | +|4 |16 | | | | | + +¹ As of ChibiOS 20.3.4, the ADC driver for STM32F1xx devices supports only ADC1, therefore any configurations involving ADC2 or ADC3 cannot actually be used. In particular, pins `F6`…`F10`, which are present at least on some STM32F103x[C-G] devices, cannot be used as ADC inputs because of this driver limitation. + +² Not all STM32F4xx devices have ADC2 and/or ADC3, therefore some configurations shown in this table may be unavailable; in particular, pins `F4`…`F10` cannot be used as ADC inputs on devices which do not have ADC3. Check the device datasheet to confirm which pin functions are supported. + +#### RP2040 + +RP2040 has only a single ADC (`ADCD1` in ChibiOS); in the QMK API the index for that ADC is 0. + +|Channel|Pin | +|-------|-------------------| +|0 |`GP26` | +|1 |`GP27` | +|2 |`GP28` | +|3 |`GP29` | +|4 |Temperature sensor*| + + +* The temperature sensor is disabled by default and needs to be enabled by the RP2040-specific function: `adcRPEnableTS(&ADCD1)`. The ADC must be initialized before calling that function; an easy way to ensure that is to perform a dummy conversion. + +## Functions + +### AVR + +|Function |Description | +|----------------------------|-------------------------------------------------------------------------------------------------------------------| +|`analogReference(mode)` |Sets the analog voltage reference source. Must be one of `ADC_REF_EXTERNAL`, `ADC_REF_POWER` or `ADC_REF_INTERNAL`.| +|`analogReadPin(pin)` |Reads the value from the specified pin, eg. `F6` for ADC6 on the ATmega32U4. | +|`pinToMux(pin)` |Translates a given pin to a mux value. If an unsupported pin is given, returns the mux value for "0V (GND)". | +|`adc_read(mux)` |Reads the value from the ADC according to the specified mux. See your MCU's datasheet for more information. | + +### ARM + +|Function |Description | +|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|`analogReadPin(pin)` |Reads the value from the specified pin, eg. `A0` for channel 0 on the STM32F0 and ADC1 channel 1 on the STM32F3. Note that if a pin can be used for multiple ADCs, it will pick the lower numbered ADC for this function. eg. `C0` will be channel 6 of ADC 1 when it could be used for ADC 2 as well.| +|`analogReadPinAdc(pin, adc)`|Reads the value from the specified pin and ADC, eg. `C0, 1` will read from channel 6, ADC 2 instead of ADC 1. Note that the ADCs are 0-indexed for this function. | +|`pinToMux(pin)` |Translates a given pin to a channel and ADC combination. If an unsupported pin is given, returns the mux value for "0V (GND)". | +|`adc_read(mux)` |Reads the value from the ADC according to the specified pin and ADC combination. See your MCU's datasheet for more information. | + +## Configuration + +## ARM + +The ARM implementation of the ADC has a few additional options that you can override in your own keyboards and keymaps to change how it operates. Please consult the corresponding `hal_adc_lld.h` in ChibiOS for your specific microcontroller for further documentation on your available options. + +|`#define` |Type |Default |Description | +|---------------------|------|----------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|`ADC_CIRCULAR_BUFFER`|`bool`|`false` |If `true`, then the implementation will use a circular buffer. | +|`ADC_NUM_CHANNELS` |`int` |`1` |Sets the number of channels that will be scanned as part of an ADC operation. The current implementation only supports `1`. | +|`ADC_BUFFER_DEPTH` |`int` |`2` |Sets the depth of each result. Since we are only getting a 10-bit result by default, we set this to 2 bytes so we can contain our one value. This could be set to 1 if you opt for an 8-bit or lower result.| +|`ADC_SAMPLING_RATE` |`int` |`ADC_SMPR_SMP_1P5` |Sets the sampling rate of the ADC. By default, it is set to the fastest setting. | +|`ADC_RESOLUTION` |`int` |`ADC_CFGR1_RES_10BIT` or `ADC_CFGR_RES_10BITS`|The resolution of your result. We choose 10 bit by default, but you can opt for 12, 10, 8, or 6 bit. Different MCUs use slightly different names for the resolution constants. | diff --git a/docs/drivers/apa102.md b/docs/drivers/apa102.md new file mode 100644 index 0000000000..88868a73b5 --- /dev/null +++ b/docs/drivers/apa102.md @@ -0,0 +1,49 @@ +# APA102 Driver {#apa102-driver} + +This driver provides support for APA102 addressable RGB LEDs. They are similar to the [WS2812](ws2812) LEDs, but have increased data and refresh rates. + +## Usage {#usage} + +In most cases, the APA102 driver code is automatically included if you are using either the [RGBLight](../features/rgblight) or [RGB Matrix](../features/rgb_matrix) feature with the `apa102` driver set, and you would use those APIs instead. + +However, if you need to use the driver standalone, add the following to your `rules.mk`: + +```make +APA102_DRIVER_REQUIRED = yes +``` + +You can then call the APA102 API by including `apa102.h` in your code. + +## Basic Configuration {#basic-configuration} + +Add the following to your `config.h`: + +|Define |Default |Description | +|---------------------------|-------------|------------------------------------------------------------------| +|`APA102_DI_PIN` |*Not defined*|The GPIO pin connected to the DI pin of the first LED in the chain| +|`APA102_CI_PIN` |*Not defined*|The GPIO pin connected to the CI pin of the first LED in the chain| +|`APA102_DEFAULT_BRIGHTNESS`|`31` |The default global brightness level of the LEDs, from 0 to 31 | + +## API {#api} + +### `void apa102_setleds(rgb_led_t *start_led, uint16_t num_leds)` + +Send RGB data to the APA102 LED chain. + +#### Arguments {#api-apa102-setleds-arguments} + + - `rgb_led_t *start_led` + A pointer to the LED array. + - `uint16_t num_leds` + The length of the LED array. + +--- + +### `void apa102_set_brightness(uint8_t brightness)` + +Set the global brightness. + +#### Arguments {#api-apa102-set-brightness-arguments} + + - `uint8_t brightness` + The brightness level to set, from 0 to 31. diff --git a/docs/drivers/audio.md b/docs/drivers/audio.md new file mode 100644 index 0000000000..c764c97369 --- /dev/null +++ b/docs/drivers/audio.md @@ -0,0 +1,239 @@ +# Audio Driver {#audio-driver} + +The [Audio feature](../features/audio) breaks the hardware specifics out into separate, exchangeable driver units, with a common interface to the audio-"core" - which itself handles playing songs and notes while tracking their progress in an internal state, initializing/starting/stopping the driver as needed. + +Not all MCUs support every available driver, either the platform-support is not there (yet?) or the MCU simply does not have the required hardware peripheral. + + +## AVR {#avr} + +Boards built around an Atmega32U4 can use two sets of PWM capable pins, each driving a separate speaker. +The possible configurations are: + +| | Timer3 | Timer1 | +|--------------|-------------|--------------| +| one speaker | C4,C5 or C6 | | +| one speaker | | B4, B5 or B7 | +| two speakers | C4,C5 or C6 | B4, B5 or B7 | + +Currently there is only one/default driver for AVR based boards, which is automatically configured to: + +```make +AUDIO_DRIVER = pwm_hardware +``` + + +## ARM {#arm} + +For Arm based boards, QMK depends on ChibiOS - hence any MCU supported by the later is likely usable, as long as certain hardware peripherals are available. + +Supported wiring configurations, with their ChibiOS/MCU peripheral requirement are listed below; +piezo speakers are marked with :one: for the first/primary and :two: for the secondary. + + | driver | GPTD6
Tim6 | GPTD7
Tim7 | GPTD8
Tim8 | PWMD11
Tim1_Ch1 | + |--------------|------------------------------------------|------------------------|---------------|-------------------------------| + | dac_basic | A4+DACD1 = :one: | A5+DACD2 = :one: | state | | + | | A4+DACD1 = :one: + Gnd | A5+DACD2 = :two: + Gnd | state | | + | | A4+DACD1 = :two: + Gnd | A5+DACD2 = :one: + Gnd | state | | + | | A4+DACD1 = :one: + Gnd | | state | | + | | | A5+DACD2 = :one: + Gnd | state | | + | dac_additive | A4+DACD1 = :one: + Gnd | | | | + | | A5+DACD2 = :one: + Gnd | | | | + | | A4+DACD1 + A5+DACD2 = :one: 2 | | | | + | pwm_software | state-update | | | any = :one: | + | pwm hardware | state-update | | | A8 = :one: 3 | + + +1: the routing and alternate functions for PWM differ sometimes between STM32 MCUs, if in doubt consult the data-sheet +2: one piezo connected to A4 and A5, with AUDIO_PIN_ALT_AS_NEGATIVE set +3: TIM1_CH1 = A8 on STM32F103C8, other combinations are possible, see Data-sheet. configured with: AUDIO_PWM_DRIVER and AUDIO_PWM_CHANNEL + + + +### DAC basic {#dac-basic} + +The default driver for ARM boards, in absence of an overriding configuration. +This driver needs one Timer per enabled/used DAC channel, to trigger conversion; and a third timer to trigger state updates with the audio-core. + +Additionally, in the board config, you'll want to make changes to enable the DACs, GPT for Timers 6, 7 and 8: + +```c +//halconf.h: +#define HAL_USE_DAC TRUE +#define HAL_USE_GPT TRUE +#include_next +``` + +```c +// mcuconf.h: +#include_next +#undef STM32_DAC_USE_DAC1_CH1 +#define STM32_DAC_USE_DAC1_CH1 TRUE +#undef STM32_DAC_USE_DAC1_CH2 +#define STM32_DAC_USE_DAC1_CH2 TRUE +#undef STM32_GPT_USE_TIM6 +#define STM32_GPT_USE_TIM6 TRUE +#undef STM32_GPT_USE_TIM7 +#define STM32_GPT_USE_TIM7 TRUE +#undef STM32_GPT_USE_TIM8 +#define STM32_GPT_USE_TIM8 TRUE +``` + +::: tip +Note: DAC1 (A4) uses TIM6, DAC2 (A5) uses TIM7, and the audio state timer uses TIM8 (configurable). +::: + +You can also change the timer used for the overall audio state by defining the driver. For instance: + +```c +#define AUDIO_STATE_TIMER GPTD9 +``` + +### DAC additive {#dac-additive} + +only needs one timer (GPTD6, Tim6) to trigger the DAC unit to do a conversion; the audio state updates are in turn triggered during the DAC callback. + +Additionally, in the board config, you'll want to make changes to enable the DACs, GPT for Timer 6: + +```c +//halconf.h: +#define HAL_USE_DAC TRUE +#define HAL_USE_GPT TRUE +#include_next +``` + +```c +// mcuconf.h: +#include_next +#undef STM32_DAC_USE_DAC1_CH1 +#define STM32_DAC_USE_DAC1_CH1 TRUE +#undef STM32_DAC_USE_DAC1_CH2 +#define STM32_DAC_USE_DAC1_CH2 TRUE +#undef STM32_GPT_USE_TIM6 +#define STM32_GPT_USE_TIM6 TRUE +``` + +### DAC Config + +| Define | Defaults | Description | +| -------------------------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `AUDIO_DAC_SAMPLE_MAX` | `4095U` | Highest value allowed. Lower value means lower volume. And 4095U is the upper limit, since this is limited to a 12 bit value. Only effects non-pregenerated samples. | +| `AUDIO_DAC_OFF_VALUE` | `AUDIO_DAC_SAMPLE_MAX / 2` | The value of the DAC when not playing anything. Some setups may require a high (`AUDIO_DAC_SAMPLE_MAX`) or low (`0`) value here. | +| `AUDIO_MAX_SIMULTANEOUS_TONES` | __see next table__ | The number of tones that can be played simultaneously. A value that is too high may freeze the controller or glitch out when too many tones are being played. | +| `AUDIO_DAC_SAMPLE_RATE` | __see next table__ | Effective bit rate of the DAC (in hertz), higher limits simultaneous tones, and lower sacrifices quality. | +| `AUDIO_DAC_BUFFER_SIZE` | __see next table__ | Number of samples generated every refill. Too few may cause excessive CPU load; too many may cause freezes, RAM or flash exhaustion or lags during matrix scanning. | + +There are a number of predefined quality settings that you can use, with "sane minimum" being the default. You can use custom values by simply defining the sample rate, number of simultaneous tones and buffer size, instead of using one of the listed presets. + +| Define | Sample Rate | Simultaneous tones | Buffer size | +| --------------------------------- | ----------- | ------------------- | ----------- | +| `AUDIO_DAC_QUALITY_VERY_LOW` | `11025U` | `8` | `64U` | +| `AUDIO_DAC_QUALITY_LOW` | `22050U` | `4` | `128U` | +| `AUDIO_DAC_QUALITY_HIGH` | `44100U` | `2` | `256U` | +| `AUDIO_DAC_QUALITY_VERY_HIGH` | `88200U` | `1` | `256U` | +| `AUDIO_DAC_QUALITY_SANE_MINIMUM` | `16384U` | `8` | `64U` | + +#### Notes on buffer size {#buffer-size} + +By default, the buffer size attempts to keep to these constraints: + +* The interval between buffer refills can't be too short, since the microcontroller would then only be servicing buffer refills and would freeze up. +* On the additive driver, the interval between buffer refills can't be too long, since matrix scanning would suffer lengthy pauses every so often, which would delay key presses or releases or lose some short taps altogether. +* The interval between buffer refills is kept to a minimum, which allows notes to stop as soon as possible after they should. +* For greater compatibility, the buffer size should be a power of 2. +* The buffer size being too large causes resource exhaustion leading to build failures or freezing at runtime: RAM usage (on the additive driver) or flash usage (on the basic driver). + +You can lower the buffer size if you need a bit more space in your firmware, or raise it if your keyboard freezes up. + + +```c + /* zero crossing (or approach, whereas zero == DAC_OFF_VALUE, which can be configured to anything from 0 to DAC_SAMPLE_MAX) + * ============================*=*========================== AUDIO_DAC_SAMPLE_MAX + * * * + * * * + * --------------------------------------------------------- + * * * } AUDIO_DAC_SAMPLE_MAX/100 + * --------------------------------------------------------- AUDIO_DAC_OFF_VALUE + * * * } AUDIO_DAC_SAMPLE_MAX/100 + * --------------------------------------------------------- + * * + * * * + * * * + * =====*=*================================================= 0x0 + */ +``` + + +### PWM hardware {#pwm-hardware} + +This driver uses the ChibiOS-PWM system to produce a square-wave on specific output pins that are connected to the PWM hardware. +The hardware directly toggles the pin via its alternate function. See your MCU's data-sheet for which pin can be driven by what timer - looking for TIMx_CHy and the corresponding alternate function. + +A configuration example for the STM32F103C8 would be: +```c +//halconf.h: +#define HAL_USE_PWM TRUE +#define HAL_USE_PAL TRUE +#include_next +``` + +```c +// mcuconf.h: +#include_next +#undef STM32_PWM_USE_TIM1 +#define STM32_PWM_USE_TIM1 TRUE +``` + +If we now target pin A8, looking through the data-sheet of the STM32F103C8, for the timers and alternate functions +- TIM1_CH1 = PA8 <- alternate0 +- TIM1_CH2 = PA9 +- TIM1_CH3 = PA10 +- TIM1_CH4 = PA11 + +with all this information, the configuration would contain these lines: +```c +//config.h: +#define AUDIO_PIN A8 +#define AUDIO_PWM_DRIVER PWMD1 +#define AUDIO_PWM_CHANNEL 1 +``` + +ChibiOS uses GPIOv1 for the F103, which only knows of one alternate function. +On 'larger' STM32s, GPIOv2 or GPIOv3 are used; with them it is also necessary to configure `AUDIO_PWM_PAL_MODE` to the correct alternate function for the selected pin, timer and timer-channel. + +You can also use the Complementary output (`TIMx_CHyN`) for PWM on supported controllers. To enable this functionality, you will need to make the following changes: +```c +// config.h: +#define AUDIO_PWM_COMPLEMENTARY_OUTPUT +``` + +### PWM software {#pwm-software} + +This driver uses the PWM callbacks from PWMD1 with TIM1_CH1 to toggle the selected AUDIO_PIN in software. +During the same callback, with AUDIO_PIN_ALT_AS_NEGATIVE set, the AUDIO_PIN_ALT is toggled inversely to AUDIO_PIN. This is useful for setups that drive a piezo from two pins (instead of one and Gnd). + +You can also change the timer used for software PWM by defining the driver. For instance: + +```c +#define AUDIO_STATE_TIMER GPTD8 +``` + + +### Testing Notes {#testing-notes} + +While not an exhaustive list, the following table provides the scenarios that have been partially validated: + +| | DAC basic | DAC additive | PWM hardware | PWM software | +| ------------------------ | ------------------ | ------------------ | ------------------ | ------------------ | +| Atmega32U4 | :o: | :o: | :heavy_check_mark: | :o: | +| RP2040 | :x: | :x: | :heavy_check_mark: | ? | +| STM32F103C8 (bluepill) | :x: | :x: | :heavy_check_mark: | :heavy_check_mark: | +| STM32F303CCT6 (proton-c) | :heavy_check_mark: | :heavy_check_mark: | ? | :heavy_check_mark: | +| STM32F405VG | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| L0xx | :x: (no Tim8) | ? | ? | ? | + +:heavy_check_mark: : works and was tested +:o: : does not apply +:x: : not supported by MCU + +*Other supported ChibiOS boards and/or pins may function, it will be highly chip and configuration dependent.* diff --git a/docs/drivers/eeprom.md b/docs/drivers/eeprom.md new file mode 100644 index 0000000000..82630c501d --- /dev/null +++ b/docs/drivers/eeprom.md @@ -0,0 +1,180 @@ +# EEPROM Driver Configuration {#eeprom-driver-configuration} + +The EEPROM driver can be swapped out depending on the needs of the keyboard, or whether extra hardware is present. + +Selecting the EEPROM driver is done in your keyboard's `rules.mk`: + +Driver | Description +-----------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +`EEPROM_DRIVER = vendor` (default) | Uses the on-chip driver provided by the chip manufacturer. For AVR, this is provided by avr-libc. This is supported on ARM for a subset of chips -- STM32F3xx, STM32F1xx, and STM32F072xB will be emulated by writing to flash. STM32L0xx and STM32L1xx will use the onboard dedicated true EEPROM. Other chips will generally act as "transient" below. +`EEPROM_DRIVER = i2c` | Supports writing to I2C-based 24xx EEPROM chips. See the driver section below. +`EEPROM_DRIVER = spi` | Supports writing to SPI-based 25xx EEPROM chips. See the driver section below. +`EEPROM_DRIVER = transient` | Fake EEPROM driver -- supports reading/writing to RAM, and will be discarded when power is lost. +`EEPROM_DRIVER = wear_leveling` | Frontend driver for the wear_leveling system, allowing for EEPROM emulation on top of flash -- both in-MCU and external SPI NOR flash. + +## Vendor Driver Configuration {#vendor-eeprom-driver-configuration} + +#### STM32 L0/L1 Configuration {#stm32l0l1-eeprom-driver-configuration} + +::: warning +Resetting EEPROM using an STM32L0/L1 device takes up to 1 second for every 1kB of internal EEPROM used. +::: + +`config.h` override | Description | Default Value +------------------------------------|--------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------- +`#define STM32_ONBOARD_EEPROM_SIZE` | The size of the EEPROM to use, in bytes. Erase times can be high, so it's configurable here, if not using the default value. | Minimum required to cover base _eeconfig_ data, or `1024` if VIA is enabled. + +## I2C Driver Configuration {#i2c-eeprom-driver-configuration} + +Currently QMK supports 24xx-series chips over I2C. As such, requires a working i2c_master driver configuration. You can override the driver configuration via your config.h: + +`config.h` override | Description | Default Value +------------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------ +`#define EXTERNAL_EEPROM_I2C_BASE_ADDRESS` | Base I2C address for the EEPROM -- shifted left by 1 as per i2c_master requirements | 0b10100000 +`#define EXTERNAL_EEPROM_I2C_ADDRESS(addr)` | Calculated I2C address for the EEPROM | `(EXTERNAL_EEPROM_I2C_BASE_ADDRESS)` +`#define EXTERNAL_EEPROM_BYTE_COUNT` | Total size of the EEPROM in bytes | 8192 +`#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`. + +Alternatively, there are pre-defined hardware configurations for available chips/modules: + +Module | Equivalent `#define` | Source +-----------------|---------------------------------|------------------------------------------ +CAT24C512 EEPROM | `#define EEPROM_I2C_CAT24C512` | +RM24C512C EEPROM | `#define EEPROM_I2C_RM24C512C` | +24LC32A EEPROM | `#define EEPROM_I2C_24LC32A` | +24LC64 EEPROM | `#define EEPROM_I2C_24LC64` | +24LC128 EEPROM | `#define EEPROM_I2C_24LC128` | +24LC256 EEPROM | `#define EEPROM_I2C_24LC256` | +MB85RC256V FRAM | `#define EEPROM_I2C_MB85RC256V` | + +::: tip +If you find that the EEPROM is not cooperating, ensure you've correctly shifted up your EEPROM address by 1. For example, the datasheet might state the address as `0b01010000` -- the correct value of `EXTERNAL_EEPROM_I2C_BASE_ADDRESS` needs to be `0b10100000`. +::: + +## SPI Driver Configuration {#spi-eeprom-driver-configuration} + +Currently QMK supports 25xx-series chips over SPI. As such, requires a working spi_master driver configuration. You can override the driver configuration via your config.h: + +`config.h` override | Default Value | Description +-----------------------------------------------|---------------|------------------------------------------------------------------------------------- +`#define EXTERNAL_EEPROM_SPI_SLAVE_SELECT_PIN` | _none_ | SPI Slave select pin in order to inform that the EEPROM is currently being addressed +`#define EXTERNAL_EEPROM_SPI_CLOCK_DIVISOR` | `64` | Clock divisor used to divide the peripheral clock to derive the SPI frequency +`#define EXTERNAL_EEPROM_BYTE_COUNT` | `8192` | Total size of the EEPROM in bytes +`#define EXTERNAL_EEPROM_PAGE_SIZE` | `32` | Page size of the EEPROM in bytes, as specified in the datasheet +`#define EXTERNAL_EEPROM_ADDRESS_SIZE` | `2` | The number of bytes to transmit for the memory location within the EEPROM + +Default values and extended descriptions can be found in `drivers/eeprom/eeprom_spi.h`. + +Alternatively, there are pre-defined hardware configurations for available chips/modules: + +Module | Equivalent `#define` | Source +-----------------|---------------------------------|------------------------------------------ +MB85RS64V FRAM | `define EEPROM_SPI_MB85RS64V` | + +::: warning +There's no way to determine if there is an SPI EEPROM actually responding. Generally, this will result in reads of nothing but zero. +::: + +## Transient Driver configuration {#transient-eeprom-driver-configuration} + +The only configurable item for the transient EEPROM driver is its size: + +`config.h` override | Description | Default Value +------------------------------- | ----------------------------------------- | ------------- +`#define TRANSIENT_EEPROM_SIZE` | Total size of the EEPROM storage in bytes | 64 + +Default values and extended descriptions can be found in `drivers/eeprom/eeprom_transient.h`. + +## Wear-leveling Driver Configuration {#wear_leveling-eeprom-driver-configuration} + +The wear-leveling driver uses an algorithm to minimise the number of erase cycles on the underlying MCU flash memory. + +There is no specific configuration for this driver, but the wear-leveling system used by this driver may need configuration. See the [wear-leveling configuration](#wear_leveling-configuration) section for more information. + +# Wear-leveling Configuration {#wear_leveling-configuration} + +The wear-leveling driver has a few possible _backing stores_ that may be used by adding to your keyboard's `rules.mk` file: + +Driver | Description +----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +`WEAR_LEVELING_DRIVER = embedded_flash` | This driver is used for emulating EEPROM by writing to embedded flash on the MCU. +`WEAR_LEVELING_DRIVER = spi_flash` | This driver is used to address external SPI NOR Flash peripherals. +`WEAR_LEVELING_DRIVER = rp2040_flash` | This driver is used to write to the same storage the RP2040 executes code from. +`WEAR_LEVELING_DRIVER = legacy` | This driver is the "legacy" emulated EEPROM provided in historical revisions of QMK. Currently used for STM32F0xx and STM32F4x1, but slated for deprecation and removal once `embedded_flash` support for those MCU families is complete. + +::: warning +All wear-leveling drivers require an amount of RAM equivalent to the selected logical EEPROM size. Increasing the size to 32kB of EEPROM requires 32kB of RAM, which a significant number of MCUs simply do not have. +::: + +## Wear-leveling Embedded Flash Driver Configuration {#wear_leveling-efl-driver-configuration} + +This driver performs writes to the embedded flash storage embedded in the MCU. In most circumstances, the last few of sectors of flash are used in order to minimise the likelihood of collision with program code. + +Configurable options in your keyboard's `config.h`: + +`config.h` override | Default | Description +-----------------------------------------|-------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +`#define WEAR_LEVELING_EFL_FIRST_SECTOR` | _unset_ | The first sector on the MCU to use. By default this is not defined and calculated at runtime based on the MCU. However, different flash sizes on MCUs may require custom configuration. +`#define WEAR_LEVELING_EFL_FLASH_SIZE` | _unset_ | Allows overriding the flash size available for use for wear-leveling. Under normal circumstances this is automatically calculated and should not need to be overridden. Specifying a size larger than the amount actually available in flash will usually prevent the MCU from booting. +`#define WEAR_LEVELING_LOGICAL_SIZE` | `(backing_size/2)` | Number of bytes "exposed" to the rest of QMK and denotes the size of the usable EEPROM. +`#define WEAR_LEVELING_BACKING_SIZE` | `2048` | Number of bytes used by the wear-leveling algorithm for its underlying storage, and needs to be a multiple of the logical size. +`#define BACKING_STORE_WRITE_SIZE` | _automatic_ | The byte width of the underlying write used on the MCU, and is usually automatically determined from the selected MCU family. If an error occurs in the auto-detection, you'll need to consult the MCU's datasheet and determine this value, specifying it directly. + +::: warning +If your MCU does not boot after swapping to the EFL wear-leveling driver, it's likely that the flash size is incorrectly detected, usually as an MCU with larger flash and may require overriding. +::: + +## Wear-leveling SPI Flash Driver Configuration {#wear_leveling-flash_spi-driver-configuration} + +This driver performs writes to an external SPI NOR Flash peripheral. It also requires a working configuration for the SPI NOR Flash peripheral -- see the [flash driver](flash) documentation for more information. + +Configurable options in your keyboard's `config.h`: + +`config.h` override | Default | Description +----------------------------------------------------|--------------------------------|-------------------------------------------------------------------------------------------------------------------------------- +`#define WEAR_LEVELING_EXTERNAL_FLASH_BLOCK_COUNT` | `1` | Number of blocks in the external flash used by the wear-leveling algorithm. +`#define WEAR_LEVELING_EXTERNAL_FLASH_BLOCK_OFFSET` | `0` | The index first block in the external flash used by the wear-leveling algorithm. +`#define WEAR_LEVELING_LOGICAL_SIZE` | `((block_count*block_size)/2)` | Number of bytes "exposed" to the rest of QMK and denotes the size of the usable EEPROM. Result must be <= 64kB. +`#define WEAR_LEVELING_BACKING_SIZE` | `(block_count*block_size)` | Number of bytes used by the wear-leveling algorithm for its underlying storage, and needs to be a multiple of the logical size. +`#define BACKING_STORE_WRITE_SIZE` | `8` | The write width used whenever a write is performed on the external flash peripheral. + +::: warning +There is currently a limit of 64kB for the EEPROM subsystem within QMK, so using a larger flash is not going to be beneficial as the logical size cannot be increased beyond 65536. The backing size may be increased to a larger value, but erase timing may suffer as a result. +::: + +## Wear-leveling RP2040 Driver Configuration {#wear_leveling-rp2040-driver-configuration} + +This driver performs writes to the same underlying storage that the RP2040 executes its code. + +Configurable options in your keyboard's `config.h`: + +`config.h` override | Default | Description +------------------------------------------|----------------------------|-------------------------------------------------------------------------------------------------------------------------------- +`#define WEAR_LEVELING_RP2040_FLASH_SIZE` | `PICO_FLASH_SIZE_BYTES` | Number of bytes of flash on the board. +`#define WEAR_LEVELING_RP2040_FLASH_BASE` | `(flash_size-sector_size)` | The byte-wise location that the backing storage should be located. +`#define WEAR_LEVELING_LOGICAL_SIZE` | `(backing_size/2)` | Number of bytes "exposed" to the rest of QMK and denotes the size of the usable EEPROM. +`#define WEAR_LEVELING_BACKING_SIZE` | `8192` | Number of bytes used by the wear-leveling algorithm for its underlying storage, and needs to be a multiple of the logical size as well as the sector size. +`#define BACKING_STORE_WRITE_SIZE` | `2` | The write width used whenever a write is performed on the external flash peripheral. + +## Wear-leveling Legacy EEPROM Emulation Driver Configuration {#wear_leveling-legacy-driver-configuration} + +This driver performs writes to the embedded flash storage embedded in the MCU much like the normal Embedded Flash Driver, and is only for use with STM32F0xx and STM32F4x1 devices. This flash implementation is still currently provided as the EFL driver is currently non-functional for the previously mentioned families. + +By default, `1024` bytes of emulated EEPROM is provided: + +MCU | EEPROM Provided | Flash Used +----------|-----------------|-------------- +STM32F042 | `1024` bytes | `2048` bytes +STM32F070 | `1024` bytes | `2048` bytes +STM32F072 | `1024` bytes | `2048` bytes +STM32F401 | `1024` bytes | `16384` bytes +STM32F411 | `1024` bytes | `16384` bytes + +Under normal circumstances configuration of this driver requires intimate knowledge of the MCU's flash structure -- reconfiguration is at your own risk and will require referring to the code. diff --git a/docs/drivers/flash.md b/docs/drivers/flash.md new file mode 100644 index 0000000000..4160721350 --- /dev/null +++ b/docs/drivers/flash.md @@ -0,0 +1,26 @@ +# FLASH Driver Configuration {#flash-driver-configuration} + +The FLASH driver can be swapped out depending on the needs of the keyboard, or whether extra hardware is present. + +Driver | Description +-----------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +`FLASH_DRIVER = spi` | Supports writing to almost all NOR Flash chips. See the driver section below. + + +## SPI FLASH Driver Configuration {#spi-flash-driver-configuration} + +Currently QMK supports almost all NOR Flash chips over SPI. As such, requires a working spi_master driver configuration. You can override the driver configuration via your config.h: + +`config.h` override | Description | Default Value +-----------------------------------------------|--------------------------------------------------------------------------------------|----------------- +`#define EXTERNAL_FLASH_SPI_SLAVE_SELECT_PIN` | SPI Slave select pin in order to inform that the FLASH is currently being addressed | _none_ +`#define EXTERNAL_FLASH_SPI_CLOCK_DIVISOR` | Clock divisor used to divide the peripheral clock to derive the SPI frequency | `8` +`#define EXTERNAL_FLASH_PAGE_SIZE` | The Page size of the FLASH in bytes, as specified in the datasheet | `256` +`#define EXTERNAL_FLASH_SECTOR_SIZE` | The sector size of the FLASH in bytes, as specified in the datasheet | `(4 * 1024)` +`#define EXTERNAL_FLASH_BLOCK_SIZE` | The block size of the FLASH in bytes, as specified in the datasheet | `(64 * 1024)` +`#define EXTERNAL_FLASH_SIZE` | The total size of the FLASH in bytes, as specified in the datasheet | `(512 * 1024)` +`#define EXTERNAL_FLASH_ADDRESS_SIZE` | The Flash address size in bytes, as specified in datasheet | `3` + +::: warning +All the above default configurations are based on MX25L4006E NOR Flash. +::: diff --git a/docs/drivers/gpio.md b/docs/drivers/gpio.md new file mode 100644 index 0000000000..9ce4f2aa20 --- /dev/null +++ b/docs/drivers/gpio.md @@ -0,0 +1,44 @@ +# GPIO Control {#gpio-control} + +QMK has a GPIO control abstraction layer which is microcontroller agnostic. This is done to allow easy access to pin control across different platforms. + +## Macros {#macros} + +The following macros provide basic control of GPIOs and are found in `platforms//gpio.h`. + +|Macro |Description | +|-------------------------------------|---------------------------------------------------------------------| +|`gpio_set_pin_input(pin)` |Set pin as input with high impedance (High-Z) | +|`gpio_set_pin_input_high(pin)` |Set pin as input with builtin pull-up resistor | +|`gpio_set_pin_input_low(pin)` |Set pin as input with builtin pull-down resistor (unavailable on AVR)| +|`gpio_set_pin_output(pin)` |Set pin as output (alias of `gpio_set_pin_output_push_pull`) | +|`gpio_set_pin_output_push_pull(pin)` |Set pin as output, push/pull mode | +|`gpio_set_pin_output_open_drain(pin)`|Set pin as output, open-drain mode (unavailable on AVR and ATSAM) | +|`gpio_write_pin_high(pin)` |Set pin level as high, assuming it is an output | +|`gpio_write_pin_low(pin)` |Set pin level as low, assuming it is an output | +|`gpio_write_pin(pin, level)` |Set pin level, assuming it is an output | +|`gpio_read_pin(pin)` |Returns the level of the pin | +|`gpio_toggle_pin(pin)` |Invert pin level, assuming it is an output | + +## Advanced Settings {#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. For AVR, the standard `avr/io.h` library is used; for STM32, the ChibiOS [PAL library](https://chibios.sourceforge.net/docs3/hal/group___p_a_l.html) is used. + +## Atomic Operation {#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(void) { + // 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/drivers/i2c.md b/docs/drivers/i2c.md new file mode 100644 index 0000000000..10949ed59e --- /dev/null +++ b/docs/drivers/i2c.md @@ -0,0 +1,290 @@ +# I2C Master Driver {#i2c-master-driver} + +The I2C Master drivers used in QMK have a set of common functions to allow portability between MCUs. + +## Usage {#usage} + +In most cases, the I2C Master driver code is automatically included if you are using a feature or driver which requires it, such as [OLED](../features/oled_driver). + +However, if you need to use the driver standalone, add the following to your `rules.mk`: + +```make +I2C_DRIVER_REQUIRED = yes +``` + +You can then call the I2C API by including `i2c_master.h` in your code. + +## I2C Addressing {#note-on-i2c-addresses} + +All of the addresses expected by this driver should be pushed to the upper 7 bits of the address byte. Setting +the lower bit (indicating read/write) will be done by the respective functions. Almost all I2C addresses listed +on datasheets and the internet will be represented as 7 bits occupying the lower 7 bits and will need to be +shifted to the left (more significant) by one bit. This is easy to do via the bitwise shift operator `<< 1`. + +You can either do this on each call to the functions below, or once in your definition of the address. For example, if your device has an address of `0x18`: + +```c +#define MY_I2C_ADDRESS (0x18 << 1) +``` + +See https://www.robot-electronics.co.uk/i2c-tutorial for more information about I2C addressing and other technical details. + +## AVR Configuration {#avr-configuration} + +The following defines can be used to configure the I2C master driver: + +|`config.h` Override|Description |Default | +|-------------------|---------------------|--------| +|`F_SCL` |Clock frequency in Hz|`400000`| + +No further setup is required - just connect the `SDA` and `SCL` pins of your I2C devices to the matching pins on the MCU: + +|MCU |`SCL`|`SDA`| +|------------------|-----|-----| +|ATmega16/32U4 |`D0` |`D1` | +|AT90USB64/128 |`D0` |`D1` | +|ATmega32A |`C0` |`C1` | +|ATmega328/P |`C5` |`C4` | + +::: tip +The ATmega16/32U2 does not possess I2C functionality, and so cannot use this driver. +::: + +## ChibiOS/ARM Configuration {#arm-configuration} + +You'll need to determine which pins can be used for I2C -- a an example, STM32 parts generally have multiple I2C peripherals, labeled I2C1, I2C2, I2C3 etc. + +To enable I2C, modify your board's `halconf.h` to enable I2C: + +```c +#define HAL_USE_I2C TRUE +``` + +Then, modify your board's `mcuconf.h` to enable the peripheral you've chosen, for example: + +```c +#undef STM32_I2C_USE_I2C2 +#define STM32_I2C_USE_I2C2 TRUE +``` + +|`mcuconf.h` Setting |Description |Default| +|----------------------------|----------------------------------------------------------------------------------|-------| +|`STM32_I2C_BUSY_TIMEOUT` |Time in milliseconds until the I2C command is aborted if no response is received |`50` | +|`STM32_I2C_XXX_IRQ_PRIORITY`|Interrupt priority for hardware driver XXX (THIS IS AN EXPERT SETTING) |`10` | +|`STM32_I2C_USE_DMA` |Enable/Disable the ability of the MCU to offload the data transfer to the DMA unit|`TRUE` | +|`STM32_I2C_XXX_DMA_PRIORITY`|Priority of DMA unit for hardware driver XXX (THIS IS AN EXPERT SETTING) |`1` | + +Configuration-wise, you'll need to set up the peripheral as per your MCU's datasheet -- the defaults match the pins for a Proton-C, i.e. STM32F303. + +|`config.h` Overrride |Description |Default| +|------------------------|--------------------------------------------------------------|-------| +|`I2C_DRIVER` |I2C peripheral to use - I2C1 -> `I2CD1`, I2C2 -> `I2CD2` etc. |`I2CD1`| +|`I2C1_SCL_PIN` |The pin definition for SCL |`B6` | +|`I2C1_SCL_PAL_MODE` |The alternate function mode for SCL |`4` | +|`I2C1_SDA_PIN` |The pin definition for SDA |`B7` | +|`I2C1_SDA_PAL_MODE` |The alternate function mode for SDA |`4` | + +The following configuration values depend on the specific MCU in use. + +### I2Cv1 {#arm-configuration-i2cv1} + +* STM32F1xx +* STM32F2xx +* STM32F4xx +* STM32L0xx +* STM32L1xx + +See [this page](https://www.playembedded.org/blog/stm32-i2c-chibios/#7_I2Cv1_configuration_structure) for the I2Cv1 configuration structure. + +|`config.h` Override|Default | +|-------------------|----------------| +|`I2C1_OPMODE` |`OPMODE_I2C` | +|`I2C1_CLOCK_SPEED` |`100000` | +|`I2C1_DUTY_CYCLE` |`STD_DUTY_CYCLE`| + +### I2Cv2 {#arm-configuration-i2cv2} + +* STM32F0xx +* STM32F3xx +* STM32F7xx +* STM32L4xx + +See [this page](https://www.playembedded.org/blog/stm32-i2c-chibios/#8_I2Cv2_I2Cv3_configuration_structure) for the I2Cv2 configuration structure. + +|`config.h` Override |Default| +|---------------------|-------| +|`I2C1_TIMINGR_PRESC` |`0U` | +|`I2C1_TIMINGR_SCLDEL`|`7U` | +|`I2C1_TIMINGR_SDADEL`|`0U` | +|`I2C1_TIMINGR_SCLH` |`38U` | +|`I2C1_TIMINGR_SCLL` |`129U` | + +## API {#api} + +### `void i2c_init(void)` {#api-i2c-init} + +Initialize the I2C driver. This function must be called only once, before any of the below functions can be called. + +This function is weakly defined, meaning it can be overridden if necessary for your particular use case: + +```c +void i2c_init(void) { + gpio_set_pin_input(B6); // Try releasing special pins for a short time + gpio_set_pin_input(B7); + wait_ms(10); // Wait for the release to happen + + palSetPadMode(GPIOB, 6, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_PUPDR_PULLUP); // Set B6 to I2C function + palSetPadMode(GPIOB, 7, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_PUPDR_PULLUP); // Set B7 to I2C function +} +``` + +--- + +### `i2c_status_t i2c_transmit(uint8_t address, uint8_t *data, uint16_t length, uint16_t timeout)` {#api-i2c-transmit} + +Send multiple bytes to the selected I2C device. + +#### Arguments {#api-i2c-transmit-arguments} + + - `uint8_t address` + The 7-bit I2C address of the device. + - `uint8_t *data` + A pointer to the data to transmit. + - `uint16_t length` + The number of bytes to write. Take care not to overrun the length of `data`. + - `uint16_t timeout` + The time in milliseconds to wait for a response from the target device. + +#### Return Value {#api-i2c-transmit-return} + +`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`. + +--- + +### `i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-receive} + +Receive multiple bytes from the selected I2C device. + +#### Arguments {#api-i2c-receive-arguments} + + - `uint8_t address` + The 7-bit I2C address of the device. + - `uint8_t *data` + A pointer to the buffer to read into. + - `uint16_t length` + The number of bytes to read. Take care not to overrun the length of `data`. + - `uint16_t timeout` + The time in milliseconds to wait for a response from the target device. + +#### Return Value {#api-i2c-receive-return} + +`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`. + +--- + +### `i2c_status_t i2c_write_register(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-write-register} + +Writes to a register with an 8-bit address on the I2C device. + +#### Arguments {#api-i2c-write-register-arguments} + + - `uint8_t devaddr` + The 7-bit I2C address of the device. + - `uint8_t regaddr` + The register address to write to. + - `uint8_t *data` + A pointer to the data to transmit. + - `uint16_t length` + The number of bytes to write. Take care not to overrun the length of `data`. + - `uint16_t timeout` + The time in milliseconds to wait for a response from the target device. + +#### Return Value {#api-i2c-write-register-return} + +`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`. + +--- + +### `i2c_status_t i2c_write_register16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-write-register16} + +Writes to a register with a 16-bit address (big endian) on the I2C device. + +#### Arguments {#api-i2c-write-register16-arguments} + + - `uint8_t devaddr` + The 7-bit I2C address of the device. + - `uint16_t regaddr` + The register address to write to. + - `uint8_t *data` + A pointer to the data to transmit. + - `uint16_t length` + The number of bytes to write. Take care not to overrun the length of `data`. + - `uint16_t timeout` + The time in milliseconds to wait for a response from the target device. + +#### Return Value {#api-i2c-write-register16-return} + +`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`. + +--- + +### `i2c_status_t i2c_read_register(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-read-register} + +Reads from a register with an 8-bit address on the I2C device. + +#### Arguments {#api-i2c-read-register-arguments} + + - `uint8_t devaddr` + The 7-bit I2C address of the device. + - `uint8_t regaddr` + The register address to read from. + - `uint16_t length` + The number of bytes to read. Take care not to overrun the length of `data`. + - `uint16_t timeout` + The time in milliseconds to wait for a response from the target device. + +#### Return Value {#api-i2c-read-register-return} + +`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`. + +--- + +### `i2c_status_t i2c_read_register16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-read-register16} + +Reads from a register with a 16-bit address (big endian) on the I2C device. + +#### Arguments {#api-i2c-read-register16-arguments} + + - `uint8_t devaddr` + The 7-bit I2C address of the device. + - `uint16_t regaddr` + The register address to read from. + - `uint16_t length` + The number of bytes to read. Take care not to overrun the length of `data`. + - `uint16_t timeout` + The time in milliseconds to wait for a response from the target device. + +#### Return Value {#api-i2c-read-register16-return} + +`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`. + +--- + +### `i2c_status_t i2c_ping_address(uint8_t address, uint16_t timeout)` {#api-i2c-ping-address} + +Pings the I2C bus for a specific address. + +On ChibiOS a "best effort" attempt is made by reading a single byte from register 0 at the requested address. This should generally work except for I2C devices that do not not respond to a register 0 read request, which will result in a false negative result (unsucessful response to ping attempt). + +This function is weakly defined, meaning it can be overridden if necessary for your particular use case: + +#### Arguments + + - `uint8_t address` + The 7-bit I2C address of the device (ie. without the read/write bit - this will be set automatically). + - `uint16_t timeout` + The time in milliseconds to wait for a response from the target device. + +#### Return Value + +`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`. diff --git a/docs/drivers/serial.md b/docs/drivers/serial.md new file mode 100644 index 0000000000..364e951b86 --- /dev/null +++ b/docs/drivers/serial.md @@ -0,0 +1,397 @@ +# 'serial' Driver + +The Serial driver powers the [Split Keyboard](../features/split_keyboard) feature. Several implementations are available that cater to the platform and capabilites of MCU in use. Note that none of the drivers support split keyboards with more than two halves. + +| Driver | AVR | ARM | Connection between halves | +| --------------------------------------- | ------------------ | ------------------ | --------------------------------------------------------------------------------------------- | +| [Bitbang](#bitbang) | :heavy_check_mark: | :heavy_check_mark: | Single wire communication. One wire is used for reception and transmission. | +| [USART Half-duplex](#usart-half-duplex) | | :heavy_check_mark: | Efficient single wire communication. One wire is used for reception and transmission. | +| [USART Full-duplex](#usart-full-duplex) | | :heavy_check_mark: | Efficient two wire communication. Two distinct wires are used for reception and transmission. | + +::: tip +Serial in this context should be read as **sending information one bit at a time**, rather than implementing UART/USART/RS485/RS232 standards. +::: + +
+ +## Bitbang + +This is the Default driver, absence of configuration assumes this driver. It works by [bit banging](https://en.wikipedia.org/wiki/Bit_banging) a GPIO pin using the CPU. It is therefore not as efficient as a dedicated hardware peripheral, which the Half-duplex and Full-duplex drivers use. + +::: warning +On ARM platforms the bitbang driver causes connection issues when using it together with the bitbang WS2812 driver. Choosing alternate drivers for both serial and WS2812 (instead of bitbang) is strongly recommended. +::: + +### Pin configuration + +``` + LEFT RIGHT ++-------+ SERIAL +-------+ +| SSP |-----------------| SSP | +| | VDD | | +| |-----------------| | +| | GND | | +| |-----------------| | ++-------+ +-------+ +``` + +One GPIO pin is needed for the bitbang driver, as only one wire is used for receiving and transmitting data. This pin is referred to as the `SOFT_SERIAL_PIN` (SSP) in the configuration. A TRS or USB cable provides enough conductors for this driver to function. + +### Setup + +To use the bitbang driver follow these steps to activate it. + +1. Change the `SERIAL_DRIVER` to `bitbang` in your keyboards `rules.mk` file: + +```make +SERIAL_DRIVER = bitbang +``` + +2. Configure the GPIO pin of your keyboard via the `config.h` file: + +```c +#define SOFT_SERIAL_PIN D0 // or D1, D2, D3, E6 +``` + +3. On ARM platforms you must turn on ChibiOS `PAL_USE_CALLBACKS` feature: + +* In `halconf.h` add the line `#define PAL_USE_CALLBACKS TRUE`. + +
+ +## USART Half-duplex + +Targeting ARM boards based on ChibiOS, where communication is offloaded to a USART hardware device that supports Half-duplex operation. The advantages over bitbanging are fast, accurate timings and reduced CPU usage. Therefore it is advised to choose Half-duplex over Bitbang if MCU is capable of utilising Half-duplex, and Full-duplex can't be used instead (e.g. lack of available GPIO pins, or imcompatible PCB design). + +### Pin configuration + +``` + LEFT RIGHT ++-------+ | | +-------+ +| | R R | | +| | | SERIAL | | | +| TX |-----------------| TX | +| | VDD | | +| |-----------------| | +| | GND | | +| |-----------------| | ++-------+ +-------+ +``` + +Only one GPIO pin is needed for the Half-duplex driver, as only one wire is used for receiving and transmitting data. This pin is referred to as the `SERIAL_USART_TX_PIN` in the configuration. Ensure that the pin chosen for split communication can operate as the TX pin of the contoller's USART peripheral. A TRS or USB cable provides enough conductors for this driver to function. As the split connection is configured to operate in open-drain mode, an **external pull-up resistor is needed to keep the line high**. Resistor values of 1.5kΩ to 8.2kΩ are known to work. + +::: warning +***Note:*** A pull-up resistor isn't required for RP2040 controllers configured with PIO subsystem. +::: + +### Setup + +To use the Half-duplex driver follow these steps to activate it. If you target the Raspberry Pi RP2040 PIO implementation, start at step 2. + +1. Change the `SERIAL_DRIVER` to `usart` in your keyboards `rules.mk` file: + +```make +SERIAL_DRIVER = usart +``` + +Skip to step 3. + +2. (RP2040 + PIO only!) Change the `SERIAL_DRIVER` to `vendor` in your keyboards `rules.mk` file: + +```make +SERIAL_DRIVER = vendor +``` + +3. Configure the hardware of your keyboard via the `config.h` file: + +```c +#define SERIAL_USART_TX_PIN B6 // The GPIO pin that is used split communication. +``` + +For STM32 MCUs several GPIO configuration options can be changed as well. See the section ["Alternate Functions for selected STM32 MCUs"](#alternate-functions-for-selected-stm32-mcus). + +```c +#define USART1_REMAP // Remap USART TX and RX pins on STM32F103 MCUs, see table below. +#define SERIAL_USART_TX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7 +``` + +4. Decide either for `SERIAL`, `SIO`, or `PIO` subsystem. See section ["Choosing a driver subsystem"](#choosing-a-driver-subsystem). + +
+ +## USART Full-duplex + +Targeting ARM boards based on ChibiOS where communication is offloaded to an USART hardware device. The advantages over bitbanging are fast, accurate timings and reduced CPU usage; therefore it is advised to choose this driver over all others where possible. Due to its internal design Full-duplex is slightly more efficient than the Half-duplex driver, but Full-duplex should be primarily chosen if Half-duplex operation is not supported by the controller's USART peripheral. + +### Pin configuration + +``` + LEFT RIGHT ++-------+ +-------+ +| | SERIAL | | +| TX |-----------------| RX | +| | SERIAL | | +| RX |-----------------| TX | +| | VDD | | +| |-----------------| | +| | GND | | +| |-----------------| | ++-------+ +-------+ +``` + +Two GPIO pins are needed for the Full-duplex driver, as two distinct wires are used for receiving and transmitting data. The pin transmitting data is the `TX` pin and refereed to as the `SERIAL_USART_TX_PIN`, the pin receiving data is the `RX` pin and refereed to as the `SERIAL_USART_RX_PIN` in this configuration. Please note that `TX` pin of the master half has to be connected with the `RX` pin of the slave half and the `RX` pin of the master half has to be connected with the `TX` pin of the slave half! Usually this pin swap has to be done outside of the MCU e.g. with cables or on the PCB. Some MCUs like the STM32F303 used on the Proton-C allow this pin swap directly inside the MCU. A TRRS or USB cable provides enough conductors for this driver to function. + +To use this driver the USART peripherals `TX` and `RX` pins must be configured with the correct Alternate-functions. If you are using a Proton-C development board everything is already setup, same is true for STM32F103 MCUs. For MCUs which are using a modern flexible GPIO configuration you have to specify these by setting `SERIAL_USART_TX_PAL_MODE` and `SERIAL_USART_RX_PAL_MODE`. Refer to the corresponding datasheets of your MCU or find those settings in the section ["Alternate Functions for selected STM32 MCUs"](#alternate-functions-for-selected-stm32-mcus). + +### Setup + +To use the Full-duplex driver follow these steps to activate it. If you target the Raspberry Pi RP2040 PIO implementation, start at step 2 + +1. Change the `SERIAL_DRIVER` to `usart` in your keyboards `rules.mk` file: + +```make +SERIAL_DRIVER = usart +``` + +Skip to step 3 + +2. (RP2040 + PIO only!) Change the `SERIAL_DRIVER` to `vendor` in your keyboards `rules.mk` file: + +```make +SERIAL_DRIVER = vendor +``` + +3. Configure the hardware of your keyboard via the `config.h` file: + +```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 +``` + +For STM32 MCUs several GPIO configuration options, including the ability for `TX` to `RX` pin swapping, can be changed as well. See the section ["Alternate Functions for selected STM32 MCUs"](#alternate-functions-for-selected-stm32-mcus). + +```c +#define SERIAL_USART_PIN_SWAP // Swap TX and RX pins if keyboard is master halve. (Only available on some MCUs) +#define USART1_REMAP // Remap USART TX and RX pins on STM32F103 MCUs, see table below. +#define SERIAL_USART_TX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7 +``` + +4. Decide either for `SERIAL`, `SIO`, or `PIO` subsystem. See section ["Choosing a driver subsystem"](#choosing-a-driver-subsystem). + +
+ +## Choosing a driver subsystem + +### The `SERIAL` driver + +The `SERIAL` Subsystem is supported for the majority of ChibiOS MCUs and should be used whenever supported. Follow these steps in order to activate it: + +1. In your keyboards `halconf.h` add: + +```c +#define HAL_USE_SERIAL TRUE +``` + +2. In your keyboards `mcuconf.h`: activate the USART peripheral that is used on your MCU. The shown example is for an STM32 MCU, so this will not work on MCUs by other manufacturers. You can find the correct names in the `mcuconf.h` files of your MCU that ship with ChibiOS. + +Just below `#include_next ` add: + +```c +#include_next + +#undef STM32_SERIAL_USE_USARTn +#define STM32_SERIAL_USE_USARTn TRUE +``` + +Where 'n' matches the peripheral number of your selected USART on the MCU. + +3. In you keyboards `config.h`: override the default USART `SERIAL` driver if you use a USART peripheral that does not belong to the default selected `SD1` driver. For instance, if you selected `STM32_SERIAL_USE_USART3` the matching driver would be `SD3`. + +```c + #define SERIAL_USART_DRIVER SD3 + ``` + +### The `SIO` driver + +The `SIO` Subsystem was added to ChibiOS with the 21.11 release and is only supported on selected MCUs. It should only be chosen when the `SERIAL` subsystem is not supported by your MCU. + +Follow these steps in order to activate it: + +1. In your keyboards `halconf.h` add: + +```c +#define HAL_USE_SIO TRUE +``` + +2. In your keyboards `mcuconf.h:` activate the USART peripheral that is used on your MCU. The shown example is for an STM32 MCU, so this will not work on MCUs by other manufacturers. You can find the correct names in the `mcuconf.h` files of your MCU that ship with ChibiOS. + +Just below `#include_next ` add: + +```c +#include_next + +#undef STM32_SIO_USE_USARTn +#define STM32_SIO_USE_USARTn TRUE +``` + +Where 'n' matches the peripheral number of your selected USART on the MCU. + +3. In the keyboard's `config.h` file: override the default USART `SIO` driver if you use a USART peripheral that does not belong to the default selected `SIOD1` driver. For instance, if you selected `STM32_SERIAL_USE_USART3` the matching driver would be `SIOD3`. + +```c + #define SERIAL_USART_DRIVER SIOD3 + ``` + +### The `PIO` driver + +The `PIO` subsystem is a Raspberry Pi RP2040 specific implementation, using an integrated PIO peripheral and is therefore only available on this MCU. Because of the flexible nature of PIO peripherals, **any** GPIO pin can be used as a `TX` or `RX` pin. Half-duplex and Full-duplex operation modes are fully supported with this driver. Half-duplex uses the built-in pull-ups and GPIO manipulation of the RP2040 to drive the line high by default, thus an external pull-up resistor **is not required**. + +Optionally, the PIO peripheral utilized for split communication can be changed with the following define in config.h: +```c +#define SERIAL_PIO_USE_PIO1 // Force the usage of PIO1 peripheral, by default the Serial implementation uses the PIO0 peripheral +``` + +The Serial PIO program uses 2 state machines, 13 instructions and the complete interrupt handler of the PIO peripheral it is running on. + +
+ +## Advanced Configuration + +There are several advanced configuration options that can be defined in your keyboards `config.h` file: + +### Baudrate + +If you're having issues or need a higher baudrate with serial communication, you can change the baudrate which in turn controls the communication speed for serial. You want to lower the baudrate if you experience failed transactions. + +```c +#define SELECT_SOFT_SERIAL_SPEED {#} +``` + +| Speed | Bitbang | Half-duplex and Full-duplex | +| ----- | -------------------------- | --------------------------- | +| `0` | 189000 baud (experimental) | 460800 baud | +| `1` | 137000 baud (default) | 230400 baud (default) | +| `2` | 75000 baud | 115200 baud | +| `3` | 39000 baud | 57600 baud | +| `4` | 26000 baud | 38400 baud | +| `5` | 20000 baud | 19200 baud | + +Alternatively you can specify the baudrate directly by defining `SERIAL_USART_SPEED`. + +### Timeout + +This is the default time window in milliseconds in which a successful communication has to complete. Usually you don't want to change this value. But you can do so anyways by defining an alternate one in your keyboards `config.h` file: + +```c +#define SERIAL_USART_TIMEOUT 20 // USART driver timeout. default 20 +``` + +
+ +## Troubleshooting + +If you're having issues withe serial communication, you can enable debug messages that will give you insights which part of the communication failed. The enable these messages add to your keyboards `config.h` file: + +```c +#define SERIAL_DEBUG +``` + +::: tip +The messages will be printed out to the `CONSOLE` output. For additional information, refer to [Debugging/Troubleshooting QMK](../faq_debug). +::: + +## Alternate Functions for selected STM32 MCUs + +Pins for USART Peripherals with + +### STM32F303 / Proton-C [Datasheet](https://www.st.com/resource/en/datasheet/stm32f303cc.pdf) + +Pin Swap available: :heavy_check_mark: + +| Pin | Function | Mode | +| ---------- | -------- | ---- | +| **USART1** | | | +| PA9 | TX | AF7 | +| PA10 | RX | AF7 | +| PB6 | TX | AF7 | +| PB7 | RX | AF7 | +| PC4 | TX | AF7 | +| PC5 | RX | AF7 | +| PE0 | TX | AF7 | +| PE1 | RX | AF7 | +| **USART2** | | | +| PA2 | TX | AF7 | +| PA3 | RX | AF7 | +| PA14 | TX | AF7 | +| PA15 | RX | AF7 | +| PB3 | TX | AF7 | +| PB4 | RX | AF7 | +| PD5 | TX | AF7 | +| PD6 | RX | AF7 | +| **USART3** | | | +| PB10 | TX | AF7 | +| PB11 | RX | AF7 | +| PC10 | TX | AF7 | +| PC11 | RX | AF7 | +| PD8 | TX | AF7 | +| PD9 | RX | AF7 | + +### STM32F072 [Datasheet](https://www.st.com/resource/en/datasheet/stm32f072c8.pdf) + +Pin Swap available: :heavy_check_mark: + +| Pin | Function | Mode | +| ------ | -------- | ---- | +| USART1 | | | +| PA9 | TX | AF1 | +| PA10 | RX | AF1 | +| PB6 | TX | AF0 | +| PB7 | RX | AF0 | +| USART2 | | | +| PA2 | TX | AF1 | +| PA3 | RX | AF1 | +| PA14 | TX | AF1 | +| PA15 | RX | AF1 | +| USART3 | | | +| PB10 | TX | AF4 | +| PB11 | RX | AF4 | +| PC4 | TX | AF1 | +| PC5 | RX | AF1 | +| PC10 | TX | AF1 | +| PC11 | RX | AF1 | +| PD8 | TX | AF0 | +| PD9 | RX | AF0 | +| USART4 | | | +| PA0 | TX | AF4 | +| PA1 | RX | AF4 | + +### STM32F103 Medium Density (C8-CB) [Datasheet](https://www.st.com/resource/en/datasheet/stm32f103c8.pdf) + +Pin Swap available: N/A + +TX Pin is always Alternate Function Push-Pull, RX Pin is always regular input pin for any USART peripheral. **For STM32F103 no additional Alternate Function configuration is necessary. QMK is already configured.** + +Pin remapping: + +The pins of USART Peripherals use default Pins that can be remapped to use other pins using the AFIO registers. Default pins are marked **bold**. Add the appropriate defines to your config.h file. + +| Pin | Function | Mode | USART_REMAP | +| ---------- | -------- | ---- | ------------------- | +| **USART1** | | | | +| **PA9** | TX | AFPP | | +| **PA10** | RX | IN | | +| PB6 | TX | AFPP | USART1_REMAP | +| PB7 | RX | IN | USART1_REMAP | +| **USART2** | | | | +| **PA2** | TX | AFPP | | +| **PA3** | RX | IN | | +| PD5 | TX | AFPP | USART2_REMAP | +| PD6 | RX | IN | USART2_REMAP | +| **USART3** | | | | +| **PB10** | TX | AFPP | | +| **PB11** | RX | IN | | +| PC10 | TX | AFPP | USART3_PARTIALREMAP | +| PC11 | RX | IN | USART3_PARTIALREMAP | +| PD8 | TX | AFPP | USART3_FULLREMAP | +| PD9 | RX | IN | USART3_FULLREMAP | diff --git a/docs/drivers/spi.md b/docs/drivers/spi.md new file mode 100644 index 0000000000..ddc35de851 --- /dev/null +++ b/docs/drivers/spi.md @@ -0,0 +1,167 @@ +# SPI Master Driver {#spi-master-driver} + +The SPI Master drivers used in QMK have a set of common functions to allow portability between MCUs. + +## Usage {#usage} + +In most cases, the SPI Master driver code is automatically included if you are using a feature or driver which requires it, such as [OLED](../features/oled_driver). + +However, if you need to use the driver standalone, add the following to your `rules.mk`: + +```make +SPI_DRIVER_REQUIRED = yes +``` + +You can then call the SPI API by including `spi_master.h` in your code. + +## AVR Configuration {#avr-configuration} + +No special setup is required - just connect the `SS`, `SCK`, `MOSI` and `MISO` pins of your SPI devices to the matching pins on the MCU: + +|MCU |`SS`|`SCK`|`MOSI`|`MISO`| +|-----------------|----|-----|------|------| +|ATmega16/32U2/4 |`B0`|`B1` |`B2` |`B3` | +|AT90USB64/128/162|`B0`|`B1` |`B2` |`B3` | +|ATmega32A |`B4`|`B7` |`B5` |`B6` | +|ATmega328/P |`B2`|`B5` |`B3` |`B4` | + +You may use more than one slave select pin, not just the `SS` pin. This is useful when you have multiple devices connected and need to communicate with them individually. +`SPI_SS_PIN` can be passed to `spi_start()` to refer to `SS`. + +## ChibiOS/ARM Configuration {#arm-configuration} + +You'll need to determine which pins can be used for SPI -- as an example, STM32 parts generally have multiple SPI peripherals, labeled SPI1, SPI2, SPI3 etc. + +To enable SPI, modify your board's `halconf.h` to enable SPI: + +```c +#define HAL_USE_SPI TRUE +#define SPI_USE_WAIT TRUE +#define SPI_SELECT_MODE SPI_SELECT_MODE_PAD +``` + +Then, modify your board's `mcuconf.h` to enable the peripheral you've chosen, for example: + +```c +#undef STM32_SPI_USE_SPI2 +#define STM32_SPI_USE_SPI2 TRUE +``` + +Configuration-wise, you'll need to set up the peripheral as per your MCU's datasheet -- the defaults match the pins for a Proton-C, i.e. STM32F303. + +|`config.h` Override|Description |Default| +|-------------------|-------------------------------------------------------------|-------| +|`SPI_DRIVER` |SPI peripheral to use - SPI1 -> `SPID1`, SPI2 -> `SPID2` etc.|`SPID2`| +|`SPI_SCK_PIN` |The pin to use for SCK |`B13` | +|`SPI_SCK_PAL_MODE` |The alternate function mode for SCK |`5` | +|`SPI_MOSI_PIN` |The pin to use for MOSI |`B15` | +|`SPI_MOSI_PAL_MODE`|The alternate function mode for MOSI |`5` | +|`SPI_MISO_PIN` |The pin to use for MISO |`B14` | +|`SPI_MISO_PAL_MODE`|The alternate function mode for MISO |`5` | + +As per the AVR configuration, you may choose any other standard GPIO as a slave select pin, which should be supplied to `spi_start()`. + +If a complete SPI interface is not required, then the following can be done to disable certain SPI pins, so they don't occupy a GPIO unnecessarily: + - in `config.h`: `#define SPI_MISO_PIN NO_PIN` + - in `config.h`: `#define SPI_MOSI_PIN NO_PIN` + - in `mcuconf.h`: `#define SPI_SELECT_MODE SPI_SELECT_MODE_NONE`, in this case the `slavePin` argument passed to `spi_start()` may be `NO_PIN` if the slave select pin is not used. + +## API {#api} + +### `void spi_init(void)` {#api-spi-init} + +Initialize the SPI driver. This function must be called only once, before any of the below functions can be called. + +--- + +### `bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor)` {#api-spi-start} + +Start an SPI transaction. + +#### Arguments {#api-spi-start-arguments} + + - `pin_t slavePin` + The QMK pin to assert as the slave select pin, eg. `B4`. + - `bool lsbFirst` + Determines the endianness of the transmission. If `true`, the least significant bit of each byte is sent first. + - `uint8_t mode` + The SPI mode to use: + + |Mode|Clock Polarity |Clock Phase | + |----|--------------------|-----------------------| + |`0` |Leading edge rising |Sample on leading edge | + |`1` |Leading edge rising |Sample on trailing edge| + |`2` |Leading edge falling|Sample on leading edge | + |`3` |Leading edge falling|Sample on trailing edge| + + - `uint16_t divisor` + The SPI clock divisor, will be rounded up to the nearest power of two. This number can be calculated by dividing the MCU's clock speed by the desired SPI clock speed. For example, an MCU running at 8 MHz wanting to talk to an SPI device at 4 MHz would set the divisor to `2`. + +#### Return Value {#api-spi-start-return} + +`false` if the supplied parameters are invalid or the SPI peripheral is already in use, or `true`. + +--- + +### `spi_status_t spi_write(uint8_t data)` {#api-spi-write} + +Write a byte to the selected SPI device. + +#### Arguments {#api-spi-write-arguments} + + - `uint8_t data` + The byte to write. + +#### Return Value {#api-spi-write-return} + +`SPI_STATUS_TIMEOUT` if the timeout period elapses, or `SPI_STATUS_SUCCESS`. + +--- + +### `spi_status_t spi_read(void)` {#api-spi-read} + +Read a byte from the selected SPI device. + +#### Return Value {#api-spi-read-return} + +`SPI_STATUS_TIMEOUT` if the timeout period elapses, or the byte read from the device. + +--- + +### `spi_status_t spi_transmit(const uint8_t *data, uint16_t length)` {#api-spi-transmit} + +Send multiple bytes to the selected SPI device. + +#### Arguments {#api-spi-transmit-arguments} + + - `const uint8_t *data` + A pointer to the data to write from. + - `uint16_t length` + The number of bytes to write. Take care not to overrun the length of `data`. + +#### Return Value {#api-spi-transmit-return} + +`SPI_STATUS_TIMEOUT` if the timeout period elapses, `SPI_STATUS_ERROR` if some other error occurs, otherwise `SPI_STATUS_SUCCESS`. + +--- + +### `spi_status_t spi_receive(uint8_t *data, uint16_t length)` {#api-spi-receive} + +Receive multiple bytes from the selected SPI device. + +#### Arguments {#api-spi-receive-arguments} + + - `uint8_t *data` + A pointer to the buffer to read into. + - `uint16_t length` + The number of bytes to read. Take care not to overrun the length of `data`. + +#### Return Value {#api-spi-receive-return} + +`SPI_STATUS_TIMEOUT` if the timeout period elapses, `SPI_STATUS_ERROR` if some other error occurs, otherwise `SPI_STATUS_SUCCESS`. + +--- + +### `void spi_stop(void)` {#api-spi-stop} + +End the current SPI transaction. This will deassert the slave select pin and reset the endianness, mode and divisor configured by `spi_start()`. diff --git a/docs/drivers/uart.md b/docs/drivers/uart.md new file mode 100644 index 0000000000..23f5b3d6e4 --- /dev/null +++ b/docs/drivers/uart.md @@ -0,0 +1,122 @@ +# UART Driver {#uart-driver} + +The UART drivers used in QMK have a set of common functions to allow portability between MCUs. + +Currently, this driver does not support enabling hardware flow control (the `RTS` and `CTS` pins) if available, but may do so in future. + +## Usage {#usage} + +In most cases, the UART driver code is automatically included if you are using a feature or driver which requires it. + +However, if you need to use the driver standalone, add the following to your `rules.mk`: + +```make +UART_DRIVER_REQUIRED = yes +``` + +You can then call the UART API by including `uart.h` in your code. + +## AVR Configuration {#avr-configuration} + +No special setup is required - just connect the `RX` and `TX` pins of your UART device to the opposite pins on the MCU: + +|MCU |`TX`|`RX`|`CTS`|`RTS`| +|-------------|----|----|-----|-----| +|ATmega16/32U2|`D3`|`D2`|`D7` |`D6` | +|ATmega16/32U4|`D3`|`D2`|`D5` |`B7` | +|AT90USB64/128|`D3`|`D2`|*n/a*|*n/a*| +|ATmega32A |`D1`|`D0`|*n/a*|*n/a*| +|ATmega328/P |`D1`|`D0`|*n/a*|*n/a*| + +## ChibiOS/ARM Configuration {#arm-configuration} + +You'll need to determine which pins can be used for UART -- as an example, STM32 parts generally have multiple UART peripherals, labeled USART1, USART2, USART3 etc. + +To enable UART, modify your board's `mcuconf.h` to enable the peripheral you've chosen, for example: + +```c +#undef STM32_SERIAL_USE_USART2 +#define STM32_SERIAL_USE_USART2 TRUE +``` + +Configuration-wise, you'll need to set up the peripheral as per your MCU's datasheet -- the defaults match the pins for a Proton-C, i.e. STM32F303. + +| `config.h` override | Description | Default Value | +| --------------------------- | --------------------------------------------------------------- | ------------- | +| `#define UART_DRIVER` | USART peripheral to use - USART1 -> `SD1`, USART2 -> `SD2` etc. | `SD1` | +| `#define UART_TX_PIN` | The pin to use for TX | `A9` | +| `#define UART_TX_PAL_MODE` | The alternate function mode for TX | `7` | +| `#define UART_RX_PIN` | The pin to use for RX | `A10` | +| `#define UART_RX_PAL_MODE` | The alternate function mode for RX | `7` | +| `#define UART_CTS_PIN` | The pin to use for CTS | `A11` | +| `#define UART_CTS_PAL_MODE` | The alternate function mode for CTS | `7` | +| `#define UART_RTS_PIN` | The pin to use for RTS | `A12` | +| `#define UART_RTS_PAL_MODE` | The alternate function mode for RTS | `7` | + +## API {#api} + +### `void uart_init(uint32_t baud)` {#api-uart-init} + +Initialize the UART driver. This function must be called only once, before any of the below functions can be called. + +#### Arguments {#api-uart-init-arguments} + + - `uint32_t baud` + The baud rate to transmit and receive at. This may depend on the device you are communicating with. Common values are 1200, 2400, 4800, 9600, 19200, 38400, 57600, and 115200. + +--- + +### `void uart_write(uint8_t data)` {#api-uart-write} + +Transmit a single byte. + +#### Arguments {#api-uart-write-arguments} + + - `uint8_t data` + The byte to write. + +--- + +### `uint8_t uart_read(void)` {#api-uart-read} + +Receive a single byte. + +#### Return Value {#api-uart-read-return} + +The byte read from the receive buffer. This function will block if the buffer is empty (ie. no data to read). + +--- + +### `void uart_transmit(const uint8_t *data, uint16_t length)` {#api-uart-transmit} + +Transmit multiple bytes. + +#### Arguments {#api-uart-transmit-arguments} + + - `const uint8_t *data` + A pointer to the data to write from. + - `uint16_t length` + The number of bytes to write. Take care not to overrun the length of `data`. + +--- + +### `void uart_receive(char *data, uint16_t length)` {#api-uart-receive} + +Receive multiple bytes. + +#### Arguments {#api-uart-receive-arguments} + + - `uint8_t *data` + A pointer to the buffer to read into. + - `uint16_t length` + The number of bytes to read. Take care not to overrun the length of `data`. + +--- + +### `bool uart_available(void)` {#api-uart-available} + +Return whether the receive buffer contains data. Call this function to determine if `uart_read()` will return data immediately. + +#### Return Value {#api-uart-available-return} + +`true` if the receive buffer length is non-zero. diff --git a/docs/drivers/ws2812.md b/docs/drivers/ws2812.md new file mode 100644 index 0000000000..61addf1917 --- /dev/null +++ b/docs/drivers/ws2812.md @@ -0,0 +1,253 @@ +# WS2812 Driver {#ws2812-driver} + +This driver provides support for WorldSemi addressable RGB(W) LEDs, and compatible equivalents: + + * WS2811, WS2812, WS2812B, WS2812C, etc. + * SK6812, SK6812MINI, SK6805 + +These LEDs are often called "addressable" because instead of using a wire per color (and per LED), each LED contains a small microchip that understands a special protocol sent over a single wire. +The LEDs can be chained together, and the remaining data is passed on to the next. In this way, you can easily control the color of many LEDs using a single GPIO. + +## Usage {#usage} + +In most cases, the WS2812 driver code is automatically included if you are using either the [RGBLight](../features/rgblight) or [RGB Matrix](../features/rgb_matrix) feature with the `ws2812` driver set, and you would use those APIs instead. + +However, if you need to use the driver standalone, add the following to your `rules.mk`: + +```make +WS2812_DRIVER_REQUIRED = yes +``` + +You can then call the WS2812 API by including `ws2812.h` in your code. + +## Basic Configuration {#basic-configuration} + +Add the following to your `config.h`: + +|Define |Default |Description | +|-------------------|-----------------------|------------------------------------------------------------------------------------------------| +|`WS2812_DI_PIN` |*Not defined* |The GPIO pin connected to the DI pin of the first LED in the chain | +|`WS2812_LED_COUNT` |*Not defined* |Number of LEDs in the WS2812 chain - automatically set when RGBLight or RGB Matrix is configured| +|`WS2812_TIMING` |`1250` |The total length of a bit (TH+TL) in nanoseconds | +|`WS2812_T1H` |`900` |The length of a "1" bit's high phase in nanoseconds | +|`WS2812_T0H` |`350` |The length of a "0" bit's high phase in nanoseconds | +|`WS2812_TRST_US` |`280` |The length of the reset phase in microseconds | +|`WS2812_BYTE_ORDER`|`WS2812_BYTE_ORDER_GRB`|The byte order of the RGB data | +|`WS2812_RGBW` |*Not defined* |Enables RGBW support (except `i2c` driver) | + +### Timing Adjustment {#timing-adjustment} + +The WS2812 LED communication protocol works by encoding a "1" bit with a long high pulse (T1H), and a "0" bit with a shorter pulse (T0H). The total cycle length of a bit is the same. +The "reset" pulse (TRST) latches the sent RGB data to all of the LEDs and denotes a completed "frame". + +Some WS2812 variants have slightly different timing parameter requirements, which can be accounted for if necessary using the above `#define`s in your `config.h`. + +### Byte Order {#byte-order} + +Some WS2812 variants may have their color components in a different physical or logical order. For example, the WS2812B-2020 has physically swapped red and green LEDs, which causes the wrong color to be displayed, because the default order of the bytes sent over the wire is defined as GRB. +If you find your LED colors are consistently swapped, you may need to change the byte order by adding the following to your `config.h`: + +```c +#define WS2812_BYTE_ORDER WS2812_BYTE_ORDER_GRB +``` + +Where the byte order may be one of: + +|Byte Order|Known Devices | +|----------|----------------------------| +|`GRB` |Most WS2812s, SK6812, SK6805| +|`RGB` |WS2812B-2020 | +|`BGR` |TM1812 | + +### RGBW Support {#rgbw-support} + +Rendering the color white with RGB LEDs is typically inconsistent due to inherent variations between each individual LED die. However, some WS2812 variants (such as SK6812RGBW) also possess a white LED along with the red, green, and blue channels, which allows for a more accurate white to be displayed. + +QMK can automatically convert the RGB data to be sent to the LEDs to mix in the white channel: + +``` +w = min(r, g, b) +r -= w +g -= w +b -= w +``` + +Thus, an RGB triplet of `255,255,255` will simply turn on the white LED fully (`0,0,0,255`). + +To enable RGBW conversion, add the following to your `config.h`: + +```c +#define WS2812_RGBW +``` + +## Driver Configuration {#driver-configuration} + +Driver selection can be configured in `rules.mk` as `WS2812_DRIVER`, or in `info.json` as `ws2812.driver`. Valid values are `bitbang` (default), `i2c`, `spi`, `pwm`, `vendor`, or `custom`. See below for information on individual drivers. + +### Bitbang Driver {#bitbang-driver} + +This is the default WS2812 driver. It operates by "bit-banging" ie. directly toggling the GPIO. + +Please note that on AVR devices, due to the tight timing requirements longer chains and/or heavy CPU loads may cause visible lag. Unfortunately this driver is usually the only option for AVR. + +```make +WS2812_DRIVER = bitbang +``` + +### I2C Driver {#i2c-driver} + +A specialized driver mainly used for PS2AVRGB (Bootmapper Client) boards, which possess an ATtiny85 that handles the WS2812 LEDs. + +```make +WS2812_DRIVER = i2c +``` + +The following `#define`s apply only to the `i2c` driver: + +|Define |Default|Description | +|--------------------|-------|---------------------------------| +|`WS2812_I2C_ADDRESS`|`0xB0` |The I2C address of the ATtiny85. | +|`WS2812_I2C_TIMEOUT`|`100` |The I2C timeout, in milliseconds.| + +### PIO Driver {#pio-driver} + +This driver is RP2040-only, and leverages the onboard PIO (programmable I/O) system and DMA to offload processing from the CPU. + +The WS2812 PIO program uses one state machine, six instructions and one DMA interrupt handler callback. Due to the implementation the time resolution for this driver is 50 ns - any value not specified in this interval will be rounded to the next matching interval. + +```make +WS2812_DRIVER = vendor +``` + +### PWM Driver {#pwm-driver} + +This driver is ARM-only, and leverages the onboard PWM peripheral and DMA to offload processing from the CPU. + +```make +WS2812_DRIVER = pwm +``` + +### SPI Driver {#spi-driver} + +This driver is ARM-only, and leverages the onboard SPI peripheral and DMA to offload processing from the CPU. The DI pin **must** be connected to the MOSI pin on the MCU, and all other SPI pins **must** be left unused. This is also very dependent on your MCU's SPI peripheral clock speed, and may or may not be possible depending on the MCU selected. + +```make +WS2812_DRIVER = spi +``` + +## ChibiOS/ARM Configuration {#arm-configuration} + +The following defines apply only to ARM devices: + +|Define |Default |Description | +|------------|------------------------------|---------------------------------------------------------------------------------| +|`WS2812_T1L`|`(WS2812_TIMING - WS2812_T1H)`|The length of a "1" bit's low phase in nanoseconds (bitbang and PIO drivers only)| +|`WS2812_T0L`|`(WS2812_TIMING - WS2812_T0H)`|The length of a "0" bit's low phase in nanoseconds (bitbang and PIO drivers only)| + +### Push-Pull and Open Drain {#push-pull-open-drain} + +By default, the GPIO used for data transmission is configured as a *push-pull* output, meaning the pin is effectively always driven either to VCC or to ground. + +For situations where the logic level voltage is lower than the power supply voltage, however, this can pose an issue. The solution is to configure the pin for *open drain* mode instead, and use a pullup resistor between the DI pin and VCC. In this mode, the MCU can only pull the GPIO *low*, or leave it floating. The pullup resistor is then responsible for pulling the line high, when the MCU is not driving the GPIO. + +To configure the DI pin for open drain configuration, add the following to your `config.h`: + +```c +#define WS2812_EXTERNAL_PULLUP +``` + +### SPI Driver {#arm-spi-driver} + +Depending on the ChibiOS board configuration, you may need to enable SPI at the keyboard level. For STM32, this would look like: + +`halconf.h`: +```c +#define HAL_USE_SPI TRUE +``` +`mcuconf.h`: +```c +#undef STM32_SPI_USE_SPI1 +#define STM32_SPI_USE_SPI1 TRUE +``` + +The following `define`s apply only to the `spi` driver: + +|Define |Default |Description | +|--------------------------------|-------------|-------------------------------------------------------------------------------| +|`WS2812_SPI_DRIVER` |`SPID1` |The SPI driver to use | +|`WS2812_SPI_MOSI_PAL_MODE` |`5` |The MOSI pin alternative function to use | +|`WS2812_SPI_SCK_PIN` |*Not defined*|The SCK pin - required for F072 and possibly others | +|`WS2812_SPI_SCK_PAL_MODE` |`5` |The SCK pin alternative function to use - required for F072 and possibly others| +|`WS2812_SPI_DIVISOR` |`16` |The divisor used to adjust the baudrate | +|`WS2812_SPI_USE_CIRCULAR_BUFFER`|*Not defined*|Enable a circular buffer for improved rendering | + +#### Setting the Baudrate {#arm-spi-baudrate} + +To adjust the SPI baudrate, you will need to derive the target baudrate from the clock tree provided by STM32CubeMX, and add the following to your `config.h`: + +```c +#define WS2812_SPI_DIVISOR 16 +``` + +Only divisors of 2, 4, 8, 16, 32, 64, 128 and 256 are supported on STM32 devices. Other MCUs may have similar constraints -- check the reference manual for your respective MCU for specifics. + +#### Circular Buffer {#arm-spi-circular-buffer} + +A circular buffer can be enabled if you experience flickering. + +To enable the circular buffer, add the following to your `config.h`: + +```c +#define WS2812_SPI_USE_CIRCULAR_BUFFER +``` + +### PIO Driver {#arm-pio-driver} + +The following `#define`s apply only to the PIO driver: + +|Define |Default |Description | +|---------------------|-------------|---------------------------------------| +|`WS2812_PIO_USE_PIO1`|*Not defined*|Use the PIO1 peripheral instead of PIO0| + +### PWM Driver {#arm-pwm-driver} + +Depending on the ChibiOS board configuration, you may need to enable PWM at the keyboard level. For STM32, this would look like: + +`halconf.h`: +```c +#define HAL_USE_PWM TRUE +``` +`mcuconf.h`: +```c +#undef STM32_PWM_USE_TIM2 +#define STM32_PWM_USE_TIM2 TRUE +``` + +The following `#define`s apply only to the `pwm` driver: + +|Define |Default |Description | +|---------------------------------|--------------------|------------------------------------------------------------------------------------------| +|`WS2812_PWM_DRIVER` |`PWMD2` |The PWM driver to use | +|`WS2812_PWM_CHANNEL` |`2` |The PWM channel to use | +|`WS2812_PWM_PAL_MODE` |`2` |The pin alternative function to use | +|`WS2812_PWM_DMA_STREAM` |`STM32_DMA1_STREAM2`|The DMA Stream for `TIMx_UP` | +|`WS2812_PWM_DMA_CHANNEL` |`2` |The DMA Channel for `TIMx_UP` | +|`WS2812_PWM_DMAMUX_ID` |*Not defined* |The DMAMUX configuration for `TIMx_UP` - only required if your MCU has a DMAMUX peripheral| +|`WS2812_PWM_COMPLEMENTARY_OUTPUT`|*Not defined* |Whether the PWM output is complementary (`TIMx_CHyN`) | + +::: tip +Using a complementary timer output (`TIMx_CHyN`) is possible only for advanced-control timers (1, 8 and 20 on STM32), and the `STM32_PWM_USE_ADVANCED` option in `mcuconf.h` must be set to `TRUE`. Complementary outputs of general-purpose timers are not supported due to ChibiOS limitations. +::: + +## API {#api} + +### `void ws2812_setleds(rgb_led_t *ledarray, uint16_t number_of_leds)` {#api-ws2812-setleds} + +Send RGB data to the WS2812 LED chain. + +#### Arguments {#api-ws2812-setleds-arguments} + + - `rgb_led_t *ledarray` + A pointer to the LED array. + - `uint16_t number_of_leds` + The length of the LED array. diff --git a/docs/easy_maker.md b/docs/easy_maker.md index 6a2f00686f..e94477322b 100644 --- a/docs/easy_maker.md +++ b/docs/easy_maker.md @@ -5,7 +5,7 @@ Have you ever needed an easy way to program a controller, such as a Proton C or There are different styles of Easy Maker available depending on your needs: * [Direct Pin](https://config.qmk.fm/#/?filter=ez_maker/direct) - Connect a single switch to a single pin -* Direct Pin + Backlight (Coming Soon) - Like Direct Pin but dedicates a single pin to [Backlight](feature_backlight) control +* Direct Pin + Backlight (Coming Soon) - Like Direct Pin but dedicates a single pin to [Backlight](features/backlight) control * Direct Pin + Numlock (Coming Soon) - Like Direct Pin but dedicates a single pin to the Numlock LED * Direct Pin + Capslock (Coming Soon) - Like Direct Pin but dedicates a single pin to the Capslock LED * Direct Pin + Encoder (Coming Soon) - Like Direct Pin but uses 2 pins to add a single rotary encoder diff --git a/docs/eeprom_driver.md b/docs/eeprom_driver.md deleted file mode 100644 index 6d13377ed8..0000000000 --- a/docs/eeprom_driver.md +++ /dev/null @@ -1,180 +0,0 @@ -# EEPROM Driver Configuration {#eeprom-driver-configuration} - -The EEPROM driver can be swapped out depending on the needs of the keyboard, or whether extra hardware is present. - -Selecting the EEPROM driver is done in your keyboard's `rules.mk`: - -Driver | Description ------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -`EEPROM_DRIVER = vendor` (default) | Uses the on-chip driver provided by the chip manufacturer. For AVR, this is provided by avr-libc. This is supported on ARM for a subset of chips -- STM32F3xx, STM32F1xx, and STM32F072xB will be emulated by writing to flash. STM32L0xx and STM32L1xx will use the onboard dedicated true EEPROM. Other chips will generally act as "transient" below. -`EEPROM_DRIVER = i2c` | Supports writing to I2C-based 24xx EEPROM chips. See the driver section below. -`EEPROM_DRIVER = spi` | Supports writing to SPI-based 25xx EEPROM chips. See the driver section below. -`EEPROM_DRIVER = transient` | Fake EEPROM driver -- supports reading/writing to RAM, and will be discarded when power is lost. -`EEPROM_DRIVER = wear_leveling` | Frontend driver for the wear_leveling system, allowing for EEPROM emulation on top of flash -- both in-MCU and external SPI NOR flash. - -## Vendor Driver Configuration {#vendor-eeprom-driver-configuration} - -#### STM32 L0/L1 Configuration {#stm32l0l1-eeprom-driver-configuration} - -::: warning -Resetting EEPROM using an STM32L0/L1 device takes up to 1 second for every 1kB of internal EEPROM used. -::: - -`config.h` override | Description | Default Value -------------------------------------|--------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------- -`#define STM32_ONBOARD_EEPROM_SIZE` | The size of the EEPROM to use, in bytes. Erase times can be high, so it's configurable here, if not using the default value. | Minimum required to cover base _eeconfig_ data, or `1024` if VIA is enabled. - -## I2C Driver Configuration {#i2c-eeprom-driver-configuration} - -Currently QMK supports 24xx-series chips over I2C. As such, requires a working i2c_master driver configuration. You can override the driver configuration via your config.h: - -`config.h` override | Description | Default Value -------------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------ -`#define EXTERNAL_EEPROM_I2C_BASE_ADDRESS` | Base I2C address for the EEPROM -- shifted left by 1 as per i2c_master requirements | 0b10100000 -`#define EXTERNAL_EEPROM_I2C_ADDRESS(addr)` | Calculated I2C address for the EEPROM | `(EXTERNAL_EEPROM_I2C_BASE_ADDRESS)` -`#define EXTERNAL_EEPROM_BYTE_COUNT` | Total size of the EEPROM in bytes | 8192 -`#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`. - -Alternatively, there are pre-defined hardware configurations for available chips/modules: - -Module | Equivalent `#define` | Source ------------------|---------------------------------|------------------------------------------ -CAT24C512 EEPROM | `#define EEPROM_I2C_CAT24C512` | -RM24C512C EEPROM | `#define EEPROM_I2C_RM24C512C` | -24LC32A EEPROM | `#define EEPROM_I2C_24LC32A` | -24LC64 EEPROM | `#define EEPROM_I2C_24LC64` | -24LC128 EEPROM | `#define EEPROM_I2C_24LC128` | -24LC256 EEPROM | `#define EEPROM_I2C_24LC256` | -MB85RC256V FRAM | `#define EEPROM_I2C_MB85RC256V` | - -::: tip -If you find that the EEPROM is not cooperating, ensure you've correctly shifted up your EEPROM address by 1. For example, the datasheet might state the address as `0b01010000` -- the correct value of `EXTERNAL_EEPROM_I2C_BASE_ADDRESS` needs to be `0b10100000`. -::: - -## SPI Driver Configuration {#spi-eeprom-driver-configuration} - -Currently QMK supports 25xx-series chips over SPI. As such, requires a working spi_master driver configuration. You can override the driver configuration via your config.h: - -`config.h` override | Default Value | Description ------------------------------------------------|---------------|------------------------------------------------------------------------------------- -`#define EXTERNAL_EEPROM_SPI_SLAVE_SELECT_PIN` | _none_ | SPI Slave select pin in order to inform that the EEPROM is currently being addressed -`#define EXTERNAL_EEPROM_SPI_CLOCK_DIVISOR` | `64` | Clock divisor used to divide the peripheral clock to derive the SPI frequency -`#define EXTERNAL_EEPROM_BYTE_COUNT` | `8192` | Total size of the EEPROM in bytes -`#define EXTERNAL_EEPROM_PAGE_SIZE` | `32` | Page size of the EEPROM in bytes, as specified in the datasheet -`#define EXTERNAL_EEPROM_ADDRESS_SIZE` | `2` | The number of bytes to transmit for the memory location within the EEPROM - -Default values and extended descriptions can be found in `drivers/eeprom/eeprom_spi.h`. - -Alternatively, there are pre-defined hardware configurations for available chips/modules: - -Module | Equivalent `#define` | Source ------------------|---------------------------------|------------------------------------------ -MB85RS64V FRAM | `define EEPROM_SPI_MB85RS64V` | - -::: warning -There's no way to determine if there is an SPI EEPROM actually responding. Generally, this will result in reads of nothing but zero. -::: - -## Transient Driver configuration {#transient-eeprom-driver-configuration} - -The only configurable item for the transient EEPROM driver is its size: - -`config.h` override | Description | Default Value -------------------------------- | ----------------------------------------- | ------------- -`#define TRANSIENT_EEPROM_SIZE` | Total size of the EEPROM storage in bytes | 64 - -Default values and extended descriptions can be found in `drivers/eeprom/eeprom_transient.h`. - -## Wear-leveling Driver Configuration {#wear_leveling-eeprom-driver-configuration} - -The wear-leveling driver uses an algorithm to minimise the number of erase cycles on the underlying MCU flash memory. - -There is no specific configuration for this driver, but the wear-leveling system used by this driver may need configuration. See the [wear-leveling configuration](#wear_leveling-configuration) section for more information. - -# Wear-leveling Configuration {#wear_leveling-configuration} - -The wear-leveling driver has a few possible _backing stores_ that may be used by adding to your keyboard's `rules.mk` file: - -Driver | Description -----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -`WEAR_LEVELING_DRIVER = embedded_flash` | This driver is used for emulating EEPROM by writing to embedded flash on the MCU. -`WEAR_LEVELING_DRIVER = spi_flash` | This driver is used to address external SPI NOR Flash peripherals. -`WEAR_LEVELING_DRIVER = rp2040_flash` | This driver is used to write to the same storage the RP2040 executes code from. -`WEAR_LEVELING_DRIVER = legacy` | This driver is the "legacy" emulated EEPROM provided in historical revisions of QMK. Currently used for STM32F0xx and STM32F4x1, but slated for deprecation and removal once `embedded_flash` support for those MCU families is complete. - -::: warning -All wear-leveling drivers require an amount of RAM equivalent to the selected logical EEPROM size. Increasing the size to 32kB of EEPROM requires 32kB of RAM, which a significant number of MCUs simply do not have. -::: - -## Wear-leveling Embedded Flash Driver Configuration {#wear_leveling-efl-driver-configuration} - -This driver performs writes to the embedded flash storage embedded in the MCU. In most circumstances, the last few of sectors of flash are used in order to minimise the likelihood of collision with program code. - -Configurable options in your keyboard's `config.h`: - -`config.h` override | Default | Description ------------------------------------------|-------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -`#define WEAR_LEVELING_EFL_FIRST_SECTOR` | _unset_ | The first sector on the MCU to use. By default this is not defined and calculated at runtime based on the MCU. However, different flash sizes on MCUs may require custom configuration. -`#define WEAR_LEVELING_EFL_FLASH_SIZE` | _unset_ | Allows overriding the flash size available for use for wear-leveling. Under normal circumstances this is automatically calculated and should not need to be overridden. Specifying a size larger than the amount actually available in flash will usually prevent the MCU from booting. -`#define WEAR_LEVELING_LOGICAL_SIZE` | `(backing_size/2)` | Number of bytes "exposed" to the rest of QMK and denotes the size of the usable EEPROM. -`#define WEAR_LEVELING_BACKING_SIZE` | `2048` | Number of bytes used by the wear-leveling algorithm for its underlying storage, and needs to be a multiple of the logical size. -`#define BACKING_STORE_WRITE_SIZE` | _automatic_ | The byte width of the underlying write used on the MCU, and is usually automatically determined from the selected MCU family. If an error occurs in the auto-detection, you'll need to consult the MCU's datasheet and determine this value, specifying it directly. - -::: warning -If your MCU does not boot after swapping to the EFL wear-leveling driver, it's likely that the flash size is incorrectly detected, usually as an MCU with larger flash and may require overriding. -::: - -## Wear-leveling SPI Flash Driver Configuration {#wear_leveling-flash_spi-driver-configuration} - -This driver performs writes to an external SPI NOR Flash peripheral. It also requires a working configuration for the SPI NOR Flash peripheral -- see the [flash driver](flash_driver) documentation for more information. - -Configurable options in your keyboard's `config.h`: - -`config.h` override | Default | Description -----------------------------------------------------|--------------------------------|-------------------------------------------------------------------------------------------------------------------------------- -`#define WEAR_LEVELING_EXTERNAL_FLASH_BLOCK_COUNT` | `1` | Number of blocks in the external flash used by the wear-leveling algorithm. -`#define WEAR_LEVELING_EXTERNAL_FLASH_BLOCK_OFFSET` | `0` | The index first block in the external flash used by the wear-leveling algorithm. -`#define WEAR_LEVELING_LOGICAL_SIZE` | `((block_count*block_size)/2)` | Number of bytes "exposed" to the rest of QMK and denotes the size of the usable EEPROM. Result must be <= 64kB. -`#define WEAR_LEVELING_BACKING_SIZE` | `(block_count*block_size)` | Number of bytes used by the wear-leveling algorithm for its underlying storage, and needs to be a multiple of the logical size. -`#define BACKING_STORE_WRITE_SIZE` | `8` | The write width used whenever a write is performed on the external flash peripheral. - -::: warning -There is currently a limit of 64kB for the EEPROM subsystem within QMK, so using a larger flash is not going to be beneficial as the logical size cannot be increased beyond 65536. The backing size may be increased to a larger value, but erase timing may suffer as a result. -::: - -## Wear-leveling RP2040 Driver Configuration {#wear_leveling-rp2040-driver-configuration} - -This driver performs writes to the same underlying storage that the RP2040 executes its code. - -Configurable options in your keyboard's `config.h`: - -`config.h` override | Default | Description -------------------------------------------|----------------------------|-------------------------------------------------------------------------------------------------------------------------------- -`#define WEAR_LEVELING_RP2040_FLASH_SIZE` | `PICO_FLASH_SIZE_BYTES` | Number of bytes of flash on the board. -`#define WEAR_LEVELING_RP2040_FLASH_BASE` | `(flash_size-sector_size)` | The byte-wise location that the backing storage should be located. -`#define WEAR_LEVELING_LOGICAL_SIZE` | `(backing_size/2)` | Number of bytes "exposed" to the rest of QMK and denotes the size of the usable EEPROM. -`#define WEAR_LEVELING_BACKING_SIZE` | `8192` | Number of bytes used by the wear-leveling algorithm for its underlying storage, and needs to be a multiple of the logical size as well as the sector size. -`#define BACKING_STORE_WRITE_SIZE` | `2` | The write width used whenever a write is performed on the external flash peripheral. - -## Wear-leveling Legacy EEPROM Emulation Driver Configuration {#wear_leveling-legacy-driver-configuration} - -This driver performs writes to the embedded flash storage embedded in the MCU much like the normal Embedded Flash Driver, and is only for use with STM32F0xx and STM32F4x1 devices. This flash implementation is still currently provided as the EFL driver is currently non-functional for the previously mentioned families. - -By default, `1024` bytes of emulated EEPROM is provided: - -MCU | EEPROM Provided | Flash Used -----------|-----------------|-------------- -STM32F042 | `1024` bytes | `2048` bytes -STM32F070 | `1024` bytes | `2048` bytes -STM32F072 | `1024` bytes | `2048` bytes -STM32F401 | `1024` bytes | `16384` bytes -STM32F411 | `1024` bytes | `16384` bytes - -Under normal circumstances configuration of this driver requires intimate knowledge of the MCU's flash structure -- reconfiguration is at your own risk and will require referring to the code. diff --git a/docs/faq_build.md b/docs/faq_build.md index 7fafff1066..54ed576c70 100644 --- a/docs/faq_build.md +++ b/docs/faq_build.md @@ -66,4 +66,4 @@ Due to how EEPROM works on ARM based chips, saved settings may no longer be vali [Planck rev6 reset EEPROM](https://cdn.discordapp.com/attachments/473506116718952450/539284620861243409/planck_rev6_default.bin) can be used to force an eeprom reset. After flashing this image, flash your normal firmware again which should restore your keyboard to _normal_ working order. [Preonic rev3 reset EEPROM](https://cdn.discordapp.com/attachments/473506116718952450/537849497313738762/preonic_rev3_default.bin) -If bootmagic is enabled in any form, you should be able to do this too (see [Bootmagic docs](feature_bootmagic) and keyboard info for specifics on how to do this). +If bootmagic is enabled in any form, you should be able to do this too (see [Bootmagic docs](features/bootmagic) and keyboard info for specifics on how to do this). diff --git a/docs/faq_debug.md b/docs/faq_debug.md index e22bc5d9ce..36374974df 100644 --- a/docs/faq_debug.md +++ b/docs/faq_debug.md @@ -4,7 +4,7 @@ This page details various common questions people have about troubleshooting the ## Debugging {#debugging} -Your keyboard will output debug information if you have `CONSOLE_ENABLE = yes` in your `rules.mk`. By default the output is very limited, but you can turn on debug mode to increase the amount of debug output. Use the `DB_TOGG` keycode in your keymap, use the [Command](feature_command) feature to enable debug mode, or add the following code to your keymap. +Your keyboard will output debug information if you have `CONSOLE_ENABLE = yes` in your `rules.mk`. By default the output is very limited, but you can turn on debug mode to increase the amount of debug output. Use the `DB_TOGG` keycode in your keymap, use the [Command](features/command) feature to enable debug mode, or add the following code to your keymap. ```c void keyboard_post_init_user(void) { diff --git a/docs/faq_keymap.md b/docs/faq_keymap.md index 0070b6d1de..56ccc6f6cc 100644 --- a/docs/faq_keymap.md +++ b/docs/faq_keymap.md @@ -45,7 +45,7 @@ QMK has a couple of features which allow you to change the behavior of your keyb Refer to the EEPROM clearing methods above, which should return those keys to normal operation. If that doesn't work, look here: * [Magic Keycodes](keycodes_magic) -* [Command](feature_command) +* [Command](features/command) ## The Menu Key Isn't Working @@ -86,7 +86,7 @@ Old vintage mechanical keyboards occasionally have lock switches but modern ones ## Input Special Characters Other Than ASCII like Cédille 'Ç' -See the [Unicode](feature_unicode) feature. +See the [Unicode](features/unicode) feature. ## `Fn` Key on macOS @@ -130,7 +130,7 @@ https://github.com/tekezo/Karabiner/issues/403 ## Esc and ` on a Single Key -See the [Grave Escape](feature_grave_esc) feature. +See the [Grave Escape](features/grave_esc) feature. ## Eject on Mac OSX diff --git a/docs/feature_advanced_keycodes.md b/docs/feature_advanced_keycodes.md index 50dc0bb281..7f5a10d1c1 100644 --- a/docs/feature_advanced_keycodes.md +++ b/docs/feature_advanced_keycodes.md @@ -160,7 +160,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) { return true; }; ``` -Alternatively, this can be done with [Key Overrides](feature_key_overrides#simple-example). +Alternatively, this can be done with [Key Overrides](features/key_overrides#simple-example). # Advanced topics {#advanced-topics} @@ -184,4 +184,4 @@ This page used to encompass a large set of features. We have moved many sections ## Key Overrides {#key-overrides} -* [Key Overrides](feature_key_overrides) +* [Key Overrides](features/key_overrides) diff --git a/docs/feature_audio.md b/docs/feature_audio.md deleted file mode 100644 index ec28860ae9..0000000000 --- a/docs/feature_audio.md +++ /dev/null @@ -1,377 +0,0 @@ -# Audio - -Your keyboard can make sounds! If you've got a spare pin you can hook up a simple speaker and make it beep. You can use those beeps to indicate layer transitions, modifiers, special keys, or just to play some funky 8bit tunes. - -To activate this feature, add `AUDIO_ENABLE = yes` to your `rules.mk`. - -## AVR based boards -On Atmega32U4 based boards, up to two simultaneous tones can be rendered. -With one speaker connected to a PWM capable pin on PORTC driven by timer 3 and the other on one of the PWM pins on PORTB driven by timer 1. - -The following pins can be configured as audio outputs in `config.h` - for one speaker set either one out of: - -* `#define AUDIO_PIN C4` -* `#define AUDIO_PIN C5` -* `#define AUDIO_PIN C6` -* `#define AUDIO_PIN B5` -* `#define AUDIO_PIN B6` -* `#define AUDIO_PIN B7` - -and *optionally*, for a second speaker, one of: -* `#define AUDIO_PIN_ALT B5` -* `#define AUDIO_PIN_ALT B6` -* `#define AUDIO_PIN_ALT B7` - -### Wiring -per speaker is - for example with a piezo buzzer - the black lead to Ground, and the red lead connected to the selected AUDIO_PIN for the primary; and similarly with AUDIO_PIN_ALT for the secondary. - - -## ARM based boards -for more technical details, see the notes on [Audio driver](audio_driver). - - -### DAC (basic) -Most STM32 MCUs have DAC peripherals, with a notable exception of the STM32F1xx series. Generally, the DAC peripheral drives pins A4 or A5. To enable DAC-based audio output on STM32 devices, add `AUDIO_DRIVER = dac_basic` to `rules.mk` and set in `config.h` either: - -`#define AUDIO_PIN A4` or `#define AUDIO_PIN A5` - -the other DAC channel can optionally be used with a secondary speaker, just set: - -`#define AUDIO_PIN_ALT A4` or `#define AUDIO_PIN_ALT A5` - -Do note though that the dac_basic driver is only capable of reproducing one tone per speaker/channel at a time, for more tones simultaneously, try the dac_additive driver. - -#### Wiring: -for two piezos, for example configured as `AUDIO_PIN A4` and `AUDIO_PIN_ALT A5` would be: red lead to A4 and black to Ground, and similarly with the second one: A5 = red, and Ground = black - -another alternative is to drive *one* piezo with both DAC pins - for an extra "push". -wiring red to A4 and black to A5 (or the other way round) and add `#define AUDIO_PIN_ALT_AS_NEGATIVE` to `config.h` - -##### Proton-C Example: -The Proton-C comes (optionally) with one 'builtin' piezo, which is wired to A4+A5. -For this board `config.h` would include these defines: - -```c -#define AUDIO_PIN A5 -#define AUDIO_PIN_ALT A4 -#define AUDIO_PIN_ALT_AS_NEGATIVE -``` - -### DAC (additive) -Another option, besides dac_basic (which produces sound through a square-wave), is to use the DAC to do additive wave synthesis. -With a number of predefined wave-forms or by providing your own implementation to generate samples on the fly. -To use this feature set `AUDIO_DRIVER = dac_additive` in your `rules.mk`, and select in `config.h` EITHER `#define AUDIO_PIN A4` or `#define AUDIO_PIN A5`. - -The used waveform *defaults* to sine, but others can be selected by adding one of the following defines to `config.h`: - -* `#define AUDIO_DAC_SAMPLE_WAVEFORM_SINE` -* `#define AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE` -* `#define AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID` -* `#define AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE` - -Should you rather choose to generate and use your own sample-table with the DAC unit, implement `uint16_t dac_value_generate(void)` with your keyboard - for an example implementation see keyboards/planck/keymaps/synth_sample or keyboards/planck/keymaps/synth_wavetable - - -### PWM (software) -if the DAC pins are unavailable (or the MCU has no usable DAC at all, like STM32F1xx); PWM can be an alternative. -Note that there is currently only one speaker/pin supported. - -set in `rules.mk`: - -`AUDIO_DRIVER = pwm_software` and in `config.h`: -`#define AUDIO_PIN C13` (can be any pin) to have the selected pin output a pwm signal, generated from a timer callback which toggles the pin in software. - -#### Wiring -the usual piezo wiring: red goes to the selected AUDIO_PIN, black goes to ground. - -OR if you can chose to drive one piezo with two pins, for example `#define AUDIO_PIN B1`, `#define AUDIO_PIN_ALT B2` in `config.h`, with `#define AUDIO_PIN_ALT_AS_NEGATIVE` - then the red lead could go to B1, the black to B2. - -### PWM (hardware) -STM32F1xx have to fall back to using PWM, but can do so in hardware; but again on currently only one speaker/pin. - -`AUDIO_DRIVER = pwm_hardware` in `rules.mk`, and in `config.h`: -`#define AUDIO_PIN A8` -`#define AUDIO_PWM_DRIVER PWMD1` -`#define AUDIO_PWM_CHANNEL 1` -(as well as `#define AUDIO_PWM_PAL_MODE 42` if you are on STM32F2 or larger) -which will use Timer 1 to directly drive pin PA8 through the PWM hardware (TIM1_CH1 = PA8). -Should you want to use the pwm-hardware on another pin and timer - be ready to dig into the STM32 data-sheet to pick the right TIMx_CHy and pin-alternate function. - - -## Tone Multiplexing -Since most drivers can only render one tone per speaker at a time (with the one exception: arm dac-additive) there also exists a "workaround-feature" that does time-slicing/multiplexing - which does what the name implies: cycle through a set of active tones (e.g. when playing chords in Music Mode) at a given rate, and put one tone at a time out through the one/few speakers that are available. - -To enable this feature, and configure a starting-rate, add the following defines to `config.h`: -```c -#define AUDIO_ENABLE_TONE_MULTIPLEXING -#define AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT 10 -``` - -The audio core offers interface functions to get/set/change the tone multiplexing rate from within `keymap.c`. - - -## Songs -There's a couple of different sounds that will automatically be enabled without any other configuration: -``` -STARTUP_SONG // plays when the keyboard starts up (audio.c) -GOODBYE_SONG // plays when you press the QK_BOOT key (quantum.c) -AG_NORM_SONG // plays when you press AG_NORM (quantum.c) -AG_SWAP_SONG // plays when you press AG_SWAP (quantum.c) -CG_NORM_SONG // plays when you press CG_NORM (quantum.c) -CG_SWAP_SONG // plays when you press CG_SWAP (quantum.c) -MUSIC_ON_SONG // plays when music mode is activated (process_music.c) -MUSIC_OFF_SONG // plays when music mode is deactivated (process_music.c) -CHROMATIC_SONG // plays when the chromatic music mode is selected (process_music.c) -GUITAR_SONG // plays when the guitar music mode is selected (process_music.c) -VIOLIN_SONG // plays when the violin music mode is selected (process_music.c) -MAJOR_SONG // plays when the major music mode is selected (process_music.c) -``` - -You can override the default songs by doing something like this in your `config.h`: - -```c -#ifdef AUDIO_ENABLE -# define STARTUP_SONG SONG(STARTUP_SOUND) -#endif -``` - -A full list of sounds can be found in [quantum/audio/song_list.h](https://github.com/qmk/qmk_firmware/blob/master/quantum/audio/song_list.h) - feel free to add your own to this list! All available notes can be seen in [quantum/audio/musical_notes.h](https://github.com/qmk/qmk_firmware/blob/master/quantum/audio/musical_notes.h). - -Additionally, if you with to maintain your own list of songs (such as ones that may be copyrighted) and not have them added to the repo, you can create a `user_song_list.h` file and place it in your keymap (or userspace) folder. This file will be automatically included, it just needs to exist. - -To play a custom sound at a particular time, you can define a song like this (near the top of the file): - -```c -float my_song[][2] = SONG(QWERTY_SOUND); -``` - -And then play your song like this: - -```c -PLAY_SONG(my_song); -``` - -Alternatively, you can play it in a loop like this: - -```c -PLAY_LOOP(my_song); -``` - -It's advised that you wrap all audio features in `#ifdef AUDIO_ENABLE` / `#endif` to avoid causing problems when audio isn't built into the keyboard. - -The available keycodes for audio are: - -|Key |Aliases |Description | -|-------------------------|---------|-------------------------------------------| -|`QK_AUDIO_ON` |`AU_ON` |Turns on Audio Feature | -|`QK_AUDIO_OFF` |`AU_OFF` |Turns off Audio Feature | -|`QK_AUDIO_TOGGLE` |`AU_TOGG`|Toggles Audio state | - -::: warning -These keycodes turn all of the audio functionality on and off. Turning it off means that audio feedback, audio clicky, music mode, etc. are disabled, completely. -::: - -## Audio Config - -| Settings | Default | Description | -|----------------------------------|----------------------|---------------------------------------------------------------------------------------------| -|`AUDIO_PIN` | *Not defined* |Configures the pin that the speaker is connected to. | -|`AUDIO_PIN_ALT` | *Not defined* |Configures the pin for a second speaker or second pin connected to one speaker. | -|`AUDIO_PIN_ALT_AS_NEGATIVE` | *Not defined* |Enables support for one speaker connected to two pins. | -|`AUDIO_INIT_DELAY` | *Not defined* |Enables delay during startup song to accomidate for USB startup issues. | -|`AUDIO_ENABLE_TONE_MULTIPLEXING` | *Not defined* |Enables time splicing/multiplexing to create multiple tones simutaneously. | -|`AUDIO_POWER_CONTROL_PIN` | *Not defined* |Enables power control code to enable or cut off power to speaker (such as with PAM8302 amp). | -|`AUDIO_POWER_CONTROL_PIN_ON_STATE`| `1` |The state of the audio power control pin when audio is "on" - `1` for high, `0` for low. | -|`STARTUP_SONG` | `STARTUP_SOUND` |Plays when the keyboard starts up (audio.c) | -|`GOODBYE_SONG` | `GOODBYE_SOUND` |Plays when you press the QK_BOOT key (quantum.c) | -|`AG_NORM_SONG` | `AG_NORM_SOUND` |Plays when you press AG_NORM (process_magic.c) | -|`AG_SWAP_SONG` | `AG_SWAP_SOUND` |Plays when you press AG_SWAP (process_magic.c) | -|`CG_NORM_SONG` | `AG_NORM_SOUND` |Plays when you press CG_NORM (process_magic.c) | -|`CG_SWAP_SONG` | `AG_SWAP_SOUND` |Plays when you press CG_SWAP (process_magic.c) | -|`MUSIC_ON_SONG` | `MUSIC_ON_SOUND` |Plays when music mode is activated (process_music.c) | -|`MUSIC_OFF_SONG` | `MUSIC_OFF_SOUND` |Plays when music mode is deactivated (process_music.c) | -|`MIDI_ON_SONG` | `MUSIC_ON_SOUND` |Plays when midi mode is activated (process_music.c) | -|`MIDI_OFF_SONG` | `MUSIC_OFF_SOUND` |Plays when midi mode is deactivated (process_music.c) | -|`CHROMATIC_SONG` | `CHROMATIC_SOUND` |Plays when the chromatic music mode is selected (process_music.c) | -|`GUITAR_SONG` | `GUITAR_SOUND` |Plays when the guitar music mode is selected (process_music.c) | -|`VIOLIN_SONG` | `VIOLIN_SOUND` |Plays when the violin music mode is selected (process_music.c) | -|`MAJOR_SONG` | `MAJOR_SOUND` |Plays when the major music mode is selected (process_music.c) | -|`DEFAULT_LAYER_SONGS` | *Not defined* |Plays song when switched default layers with [`set_single_persistent_default_layer(layer)`](ref_functions.md#setting-the-persistent-default-layer)(quantum.c). | -|`SENDSTRING_BELL` | *Not defined* |Plays chime when the "enter" ("\a") character is sent (send_string.c) | - -## Tempo -the 'speed' at which SONGs are played is dictated by the set Tempo, which is measured in beats-per-minute. Note lengths are defined relative to that. -The initial/default tempo is set to 120 bpm, but can be configured by setting `TEMPO_DEFAULT` in `config.c`. -There is also a set of functions to modify the tempo from within the user/keymap code: -```c -void audio_set_tempo(uint8_t tempo); -void audio_increase_tempo(uint8_t tempo_change); -void audio_decrease_tempo(uint8_t tempo_change); -``` - -## ARM Audio Volume - -For ARM devices, you can adjust the DAC sample values. If your board is too loud for you or your coworkers, you can set the max using `AUDIO_DAC_SAMPLE_MAX` in your `config.h`: - -```c -#define AUDIO_DAC_SAMPLE_MAX 4095U -``` -the DAC usually runs in 12Bit mode, hence a volume of 100% = 4095U - -Note: this only adjusts the volume aka 'works' if you stick to WAVEFORM_SQUARE, since its samples are generated on the fly - any other waveform uses a hardcoded/precomputed sample-buffer. - -## Voices -Aka "audio effects", different ones can be enabled by setting in `config.h` these defines: -`#define AUDIO_VOICES` to enable the feature, and `#define AUDIO_VOICE_DEFAULT something` to select a specific effect -for details see quantum/audio/voices.h and .c - -Keycodes available: - -|Key |Aliases |Description | -|-------------------------|---------|-------------------------------------------| -|`QK_AUDIO_VOICE_NEXT` |`AU_NEXT`|Cycles through the audio voices | -|`QK_AUDIO_VOICE_PREVIOUS`|`AU_PREV`|Cycles through the audio voices in reverse | - -## Music Mode - -The music mode maps your columns to a chromatic scale, and your rows to octaves. This works best with ortholinear keyboards, but can be made to work with others. All keycodes less than `0xFF` get blocked, so you won't type while playing notes - if you have special keys/mods, those will still work. A work-around for this is to jump to a different layer with KC_NOs before (or after) enabling music mode. - -Recording is experimental due to some memory issues - if you experience some weird behavior, unplugging/replugging your keyboard will fix things. - -Keycodes available: - -|Key |Aliases |Description | -|-------------------------|---------|-------------------------------------------| -|`QK_MUSIC_ON` |`MU_ON` |Turns on Music Mode | -|`QK_MUSIC_OFF` |`MU_OFF` |Turns off Music Mode | -|`QK_MUSIC_TOGGLE` |`MU_TOGG`|Toggles Music Mode | -|`QK_MUSIC_MODE_NEXT` |`MU_NEXT`|Cycles through the music modes | - -Available Modes: - * `CHROMATIC_MODE` - Chromatic scale, row changes the octave - * `GUITAR_MODE` - Chromatic scale, but the row changes the string (+5 st) - * `VIOLIN_MODE` - Chromatic scale, but the row changes the string (+7 st) - * `MAJOR_MODE` - Major scale - -In music mode, the following keycodes work differently, and don't pass through: - -* `LCTL` - start a recording -* `LALT` - stop recording/stop playing -* `LGUI` - play recording -* `KC_UP` - speed-up playback -* `KC_DOWN` - slow-down playback - -The pitch standard (`PITCH_STANDARD_A`) is 440.0f by default - to change this, add something like this to your `config.h`: - -```c -#define PITCH_STANDARD_A 432.0f -``` - -You can completely disable Music Mode as well. This is useful, if you're pressed for space on your controller. To disable it, add this to your `config.h`: - -```c -#define NO_MUSIC_MODE -``` - -### Music Mask - -By default, `MUSIC_MASK` is set to `keycode < 0xFF` which means keycodes less than `0xFF` are turned into notes, and don't output anything. You can change this by defining this in your `config.h` like this: - -```c -#define MUSIC_MASK keycode != KC_NO -``` - -Which will capture all keycodes - be careful, this will get you stuck in music mode until you restart your keyboard! - -For a more advanced way to control which keycodes should still be processed, you can use `music_mask_kb(keycode)` in `.c` and `music_mask_user(keycode)` in your `keymap.c`: - -```c - bool music_mask_user(uint16_t keycode) { - switch (keycode) { - case RAISE: - case LOWER: - return false; - default: - return true; - } - } -``` - -Things that return false are not part of the mask, and are always processed. - -### Music Map - -By default, the Music Mode uses the columns and row to determine the scale for the keys. For a board that uses a rectangular matrix that matches the keyboard layout, this is just fine. However, for boards that use a more complicated matrix (such as the Planck Rev6, or many split keyboards) this would result in a very skewed experience. - -However, the Music Map option allows you to remap the scaling for the music mode, so it fits the layout, and is more natural. - -To enable this feature, add `#define MUSIC_MAP` to your `config.h` file, and then you will want to add a `uint8_t music_map` to your keyboard's `c` file, or your `keymap.c`. - -```c -const uint8_t music_map[MATRIX_ROWS][MATRIX_COLS] = LAYOUT_ortho_4x12( - 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, - 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 -); -``` - -You will want to use whichever `LAYOUT` macro that your keyboard uses here. This maps it to the correct key location. Start in the bottom left of the keyboard layout, and move to the right, and then upwards. Fill in all the entries until you have a complete matrix. - -You can look at the [Planck Keyboard](https://github.com/qmk/qmk_firmware/blob/e9ace1487887c1f8b4a7e8e6d87c322988bec9ce/keyboards/planck/planck.c#L24-L29) as an example of how to implement this. - -## Audio Click - -This adds a click sound each time you hit a button, to simulate click sounds from the keyboard. And the sounds are slightly different for each keypress, so it doesn't sound like a single long note, if you type rapidly. - -Keycodes available: - -|Key |Aliases |Description | -|-------------------------|---------|-------------------------------------------| -|`QK_AUDIO_CLICKY_TOGGLE` |`CK_TOGG`|Toggles Audio clicky mode | -|`QK_AUDIO_CLICKY_ON` |`CK_ON` |Turns on Audio clicky mode | -|`QK_AUDIO_CLICKY_OFF` |`CK_OFF` |Turns on Audio clicky mode | -|`QK_AUDIO_CLICKY_UP` |`CK_UP` |Increases frequency of the clicks | -|`QK_AUDIO_CLICKY_DOWN` |`CK_DOWN`|Decreases frequency of the clicks | -|`QK_AUDIO_CLICKY_RESET` |`CK_RST` |Resets frequency to default | - -The feature is disabled by default, to save space. To enable it, add this to your `config.h`: - -```c -#define AUDIO_CLICKY -``` - -You can configure the default, min and max frequencies, the stepping and built in randomness by defining these values: - -| Option | Default Value | Description | -|--------|---------------|-------------| -| `AUDIO_CLICKY_FREQ_DEFAULT` | 440.0f | Sets the default/starting audio frequency for the clicky sounds. | -| `AUDIO_CLICKY_FREQ_MIN` | 65.0f | Sets the lowest frequency (under 60f are a bit buggy). | -| `AUDIO_CLICKY_FREQ_MAX` | 1500.0f | Sets the highest frequency. Too high may result in coworkers attacking you. | -| `AUDIO_CLICKY_FREQ_FACTOR` | 1.18921f| Sets the stepping of UP/DOWN key codes. This is a multiplicative factor. The default steps the frequency up/down by a musical minor third. | -| `AUDIO_CLICKY_FREQ_RANDOMNESS` | 0.05f | Sets a factor of randomness for the clicks, Setting this to `0f` will make each click identical, and `1.0f` will make this sound much like the 90's computer screen scrolling/typing effect. | -| `AUDIO_CLICKY_DELAY_DURATION` | 1 | An integer note duration where 1 is 1/16th of the tempo, or a sixty-fourth note (see `quantum/audio/musical_notes.h` for implementation details). The main clicky effect will be delayed by this duration. Adjusting this to values around 6-12 will help compensate for loud switches. | - -## MIDI Functionality - -See [MIDI](feature_midi) - -## Audio Keycodes - -|Key |Aliases |Description | -|-------------------------|---------|-------------------------------------------| -|`QK_AUDIO_ON` |`AU_ON` |Turns on Audio Feature | -|`QK_AUDIO_OFF` |`AU_OFF` |Turns off Audio Feature | -|`QK_AUDIO_TOGGLE` |`AU_TOGG`|Toggles Audio state | -|`QK_AUDIO_CLICKY_TOGGLE` |`CK_TOGG`|Toggles Audio clicky mode | -|`QK_AUDIO_CLICKY_ON` |`CK_ON` |Turns on Audio clicky mode | -|`QK_AUDIO_CLICKY_OFF` |`CK_OFF` |Turns on Audio clicky mode | -|`QK_AUDIO_CLICKY_UP` |`CK_UP` |Increases frequency of the clicks | -|`QK_AUDIO_CLICKY_DOWN` |`CK_DOWN`|Decreases frequency of the clicks | -|`QK_AUDIO_CLICKY_RESET` |`CK_RST` |Resets frequency to default | -|`QK_MUSIC_ON` |`MU_ON` |Turns on Music Mode | -|`QK_MUSIC_OFF` |`MU_OFF` |Turns off Music Mode | -|`QK_MUSIC_TOGGLE` |`MU_TOGG`|Toggles Music Mode | -|`QK_MUSIC_MODE_NEXT` |`MU_NEXT`|Cycles through the music modes | -|`QK_AUDIO_VOICE_NEXT` |`AU_NEXT`|Cycles through the audio voices | -|`QK_AUDIO_VOICE_PREVIOUS`|`AU_PREV`|Cycles through the audio voices in reverse | diff --git a/docs/feature_auto_shift.md b/docs/feature_auto_shift.md deleted file mode 100644 index 53635e39f0..0000000000 --- a/docs/feature_auto_shift.md +++ /dev/null @@ -1,398 +0,0 @@ -# Auto Shift: Why Do We Need a Shift Key? - -Tap a key and you get its character. Tap a key, but hold it *slightly* longer -and you get its shifted state. Voilà! No shift key needed! - -## Why Auto Shift? - -Many people suffer from various forms of RSI. A common cause is stretching your -fingers repetitively long distances. For us on the keyboard, the pinky does that -all too often when reaching for the shift key. Auto Shift looks to alleviate that -problem. - -## How Does It Work? - -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 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). - -There are also the `get_auto_shift_repeat` and `get_auto_shift_no_auto_repeat` -functions for more granular control. Neither will have an effect unless -`AUTO_SHIFT_REPEAT_PER_KEY` or `AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY` respectively -are defined. - -## Are There Limitations to Auto Shift? - -Yes, unfortunately. - -1. 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. -2. 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. -3. Auto Shift does not apply to Tap Hold keys. For automatic shifting of Tap Hold - keys see [Retro Shift](#retro-shift). - -## How Do I Enable Auto Shift? - -Add to your `rules.mk` in the keymap folder: - -``` -AUTO_SHIFT_ENABLE = yes -``` - -If no `rules.mk` exists, you can create one. - -Then compile and install your new firmware with Auto Key enabled! That's it! - -## Modifiers - -By default, Auto Shift is disabled for any key press that is accompanied by one or more -modifiers. Thus, Ctrl+A that you hold for a really long time is not the same -as Ctrl+Shift+A. - -You can re-enable Auto Shift for modifiers by adding a define to your `config.h` - -```c -#define AUTO_SHIFT_MODIFIERS -``` - -In which case, Ctrl+A held past the `AUTO_SHIFT_TIMEOUT` will be sent as Ctrl+Shift+A - - -## Configuring Auto Shift - -If desired, there is some configuration that can be done to change the -behavior of Auto Shift. This is done by setting various variables the -`config.h` file located in your keymap folder. If no `config.h` file exists, you can create one. - -A sample is - -```c -#pragma once - -#define AUTO_SHIFT_TIMEOUT 150 -#define NO_AUTO_SHIFT_SPECIAL -``` - -### AUTO_SHIFT_TIMEOUT (Value in ms) - -This controls how long you have to hold a key before you get the shifted state. -Obviously, this is different for everyone. For the common person, a setting of -135 to 150 works great. However, one should start with a value of at least 175, which -is the default value. Then work down from there. The idea is to have the shortest time required to get the shifted state without having false positives. - -Play with this value until things are perfect. Many find that all will work well -at a given value, but one or two keys will still emit the shifted state on -occasion. This is simply due to habit and holding some keys a little longer -than others. Once you find this value, work on tapping your problem keys a little -quicker than normal and you will be set. - -::: tip -Auto Shift has three special keys that can help you get this value right very quick. See "Auto Shift Setup" for more details! -::: - -For more granular control of this feature, you can add the following to your `config.h`: - -```c -#define AUTO_SHIFT_TIMEOUT_PER_KEY -``` - -You can then add the following function to your keymap: - -```c -uint16_t get_autoshift_timeout(uint16_t keycode, keyrecord_t *record) { - switch(keycode) { - case AUTO_SHIFT_NUMERIC: - return 2 * get_generic_autoshift_timeout(); - case AUTO_SHIFT_SPECIAL: - return get_generic_autoshift_timeout() + 50; - case AUTO_SHIFT_ALPHA: - default: - return get_generic_autoshift_timeout(); - } -} -``` - -Note that you cannot override individual keys that are in one of those groups -if you are using them; trying to add a case for `KC_A` in the above example will -not compile as `AUTO_SHIFT_ALPHA` is there. A possible solution is a second switch -above to handle individual keys with no default case and only referencing the -groups in the below fallback switch. - -### NO_AUTO_SHIFT_SPECIAL (simple define) - -Do not Auto Shift special keys, which include -\_, =+, [{, ]}, ;:, '", ,<, .>, -/?, and the KC_TAB. - -### NO_AUTO_SHIFT_TAB (simple define) - -Do not Auto Shift KC_TAB but leave Auto Shift enabled for the other special -characters. - -### NO_AUTO_SHIFT_SYMBOLS (simple define) - -Do not Auto Shift symbol keys, which include -\_, =+, [{, ]}, ;:, '", ,<, .>, -and /?. - -### NO_AUTO_SHIFT_NUMERIC (simple define) - -Do not Auto Shift numeric keys, zero through nine. - -### NO_AUTO_SHIFT_ALPHA (simple define) - -Do not Auto Shift alpha characters, which include A through Z. - -### AUTO_SHIFT_ENTER (simple define) - -Auto Shift the enter key. - -### Auto Shift Per Key - -There are functions that allows you to determine which keys should be autoshifted, much like the tap-hold keys. - -The first of these, used to simply add a key to Auto Shift, is `get_custom_auto_shifted_key`: - -```c -bool get_custom_auto_shifted_key(uint16_t keycode, keyrecord_t *record) { - switch(keycode) { - case KC_DOT: - return true; - default: - return false; - } -} -``` - -For more granular control, there is `get_auto_shifted_key`. The default function looks like this: - -```c -bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) { - switch (keycode) { -# ifndef NO_AUTO_SHIFT_ALPHA - case AUTO_SHIFT_ALPHA: -# endif -# ifndef NO_AUTO_SHIFT_NUMERIC - case AUTO_SHIFT_NUMERIC: -# endif -# ifndef NO_AUTO_SHIFT_SPECIAL -# ifndef NO_AUTO_SHIFT_TAB - case KC_TAB: -# endif -# ifndef NO_AUTO_SHIFT_SYMBOLS - case AUTO_SHIFT_SYMBOLS: -# endif -# endif -# ifdef AUTO_SHIFT_ENTER - case KC_ENT: -# endif - return true; - } - return get_custom_auto_shifted_key(keycode, record); -} -``` - -This functionality is enabled by default, and does not need a define. - -### AUTO_SHIFT_REPEAT (simple define) - -Enables keyrepeat. - -### AUTO_SHIFT_NO_AUTO_REPEAT (simple define) - -Disables automatically keyrepeating when `AUTO_SHIFT_TIMEOUT` is exceeded. - - -### AUTO_SHIFT_ALPHA (predefined key group) - -A predefined group of keys representing A through Z. - -### AUTO_SHIFT_NUMERIC (predefined key group) - -A predefined group of keys representing 0 through 9. Note, these are defined as -1 through 0 since that is the order they normally appear in. - -### AUTO_SHIFT_SYMBOLS (predefined key group) - -A predefined group of keys representing symbolic characters which include -\_, =+, [{, ]}, ;:, '", ,<, .>, -and /?. - -### AUTO_SHIFT_SPECIAL (predefined key group) - -A predefined group of keys that combines AUTO_SHIFT_SYMBOLS and KC_TAB. - -## Custom Shifted Values - -Especially on small keyboards, the default shifted value for many keys is not -optimal. To provide more customizability, there are two user-definable -functions, `autoshift_press/release_user`. These register or unregister the -correct value for the passed key. Below is an example adding period to Auto -Shift and making its shifted value exclamation point. Make sure to use weak -mods - setting real would make any keys following it use their shifted values -as if you were holding the key. Clearing of modifiers is handled by Auto Shift, -and the OS-sent shift value if keyrepeating multiple keys is always that of -the last key pressed (whether or not it's an Auto Shift key). - -You can also have non-shifted keys for the shifted values (or even no shifted -value), just don't set a shift modifier! - -```c -bool get_custom_auto_shifted_key(uint16_t keycode, keyrecord_t *record) { - switch(keycode) { - case KC_DOT: - return true; - default: - return false; - } -} - -void autoshift_press_user(uint16_t keycode, bool shifted, keyrecord_t *record) { - switch(keycode) { - case KC_DOT: - register_code16((!shifted) ? KC_DOT : KC_EXLM); - break; - default: - if (shifted) { - add_weak_mods(MOD_BIT(KC_LSFT)); - } - // & 0xFF gets the Tap key for Tap Holds, required when using Retro Shift - register_code16((IS_RETRO(keycode)) ? keycode & 0xFF : keycode); - } -} - -void autoshift_release_user(uint16_t keycode, bool shifted, keyrecord_t *record) { - switch(keycode) { - case KC_DOT: - unregister_code16((!shifted) ? KC_DOT : KC_EXLM); - break; - default: - // & 0xFF gets the Tap key for Tap Holds, required when using Retro Shift - // The IS_RETRO check isn't really necessary here, always using - // keycode & 0xFF would be fine. - unregister_code16((IS_RETRO(keycode)) ? keycode & 0xFF : keycode); - } -} -``` - -## Retro Shift - -Holding and releasing a Tap Hold key without pressing another key will ordinarily -result in only the hold. With `retro shift` enabled this action will instead -produce a shifted version of the tap keycode on release. - -It does not require [Retro Tapping](tap_hold#retro-tapping) to be enabled, and -if both are enabled the state of `retro tapping` will only apply if the tap keycode -is not matched by Auto Shift. `RETRO_TAPPING_PER_KEY` and its corresponding -function, however, are checked before `retro shift` is applied. - -To enable `retro shift`, add the following to your `config.h`: - -```c -#define RETRO_SHIFT -``` - -If `RETRO_SHIFT` is defined to a value, hold times greater than that value will -not produce a tap on release for Mod Taps, and instead triggers the hold action. -This enables modifiers to be held for combining with mouse clicks without -generating taps on release. For example: - -```c -#define RETRO_SHIFT 500 -``` - -Without a value set, holds of any length without an interrupting key will produce the shifted value. - -This value (if set) must be greater than one's `TAPPING_TERM`, as the key press -must be designated as a 'hold' by `process_tapping` before we send the modifier. -[Per-key tapping terms](tap_hold#tapping-term) can be used as a workaround. -There is no such limitation in regards to `AUTO_SHIFT_TIMEOUT` for normal keys. - -**Note:** Tap Holds must be added to Auto Shift, see [here.](feature_auto_shift#auto-shift-per-key) -`IS_RETRO` may be helpful if one wants all Tap Holds retro shifted. - -### Retro Shift and Tap Hold Configurations - -Tap Hold Configurations work a little differently when using Retro Shift. -Referencing `TAPPING_TERM` makes little sense, as holding longer would result in -shifting one of the keys. - -`RETRO_SHIFT` enables [`PERMISSIVE_HOLD`-like behaviour](tap_hold#permissive-hold) (even if not explicitly enabled) on all mod-taps for which `RETRO_SHIFT` applies. - -## Using Auto Shift Setup - -This will enable you to define three keys temporarily to increase, decrease and report your `AUTO_SHIFT_TIMEOUT`. - -### Setup - -Map three keys temporarily in your keymap: - -|Keycode |Aliases |Description | -|----------------------|---------|--------------------------------------------| -|`QK_AUTO_SHIFT_DOWN` |`AS_DOWN`|Lower the Auto Shift timeout variable (down)| -|`QK_AUTO_SHIFT_UP` |`AS_UP` |Raise the Auto Shift timeout variable (up) | -|`QK_AUTO_SHIFT_REPORT`|`AS_RPT` |Report your current Auto Shift timeout value| -|`QK_AUTO_SHIFT_ON` |`AS_ON` |Turns on the Auto Shift Function | -|`QK_AUTO_SHIFT_OFF` |`AS_OFF` |Turns off the Auto Shift Function | -|`QK_AUTO_SHIFT_TOGGLE`|`AS_TOGG`|Toggles the state of the Auto Shift feature | - -Compile and upload your new firmware. - -### Use - -It is important to note that during these tests, you should be typing -completely normal and with no intention of shifted keys. - -1. Type multiple sentences of alphabetical letters. -2. Observe any upper case letters. -3. If there are none, press the key you have mapped to `AS_DOWN` to decrease - time Auto Shift timeout value and go back to step 1. -4. If there are some upper case letters, decide if you need to work on tapping - those keys with less down time, or if you need to increase the timeout. -5. If you decide to increase the timeout, press the key you have mapped to - `AS_UP` and go back to step 1. -6. Once you are happy with your results, press the key you have mapped to - `AS_RPT`. The keyboard will type by itself the value of your - `AUTO_SHIFT_TIMEOUT`. -7. Update `AUTO_SHIFT_TIMEOUT` in your `config.h` with the value reported. -8. Add `AUTO_SHIFT_NO_SETUP` to your `config.h`. -9. Remove the key bindings `AS_DOWN`, `AS_UP` and `AS_RPT`. -10. Compile and upload your new firmware. - -#### An Example Run - -``` -hello world. my name is john doe. i am a computer programmer playing with -keyboards right now. - -[PRESS AS_DOWN quite a few times] - -heLLo woRLd. mY nAMe is JOHn dOE. i AM A compUTeR proGRaMMER PlAYiNG witH -KEYboArDS RiGHT NOw. - -[PRESS AS_UP a few times] - -hello world. my name is john Doe. i am a computer programmer playing with -keyboarDs right now. - -[PRESS AS_RPT] - -115 -``` - -The keyboard typed `115` which represents your current `AUTO_SHIFT_TIMEOUT` -value. You are now set! Practice on the *D* key a little bit that showed up -in the testing and you'll be golden. diff --git a/docs/feature_autocorrect.md b/docs/feature_autocorrect.md deleted file mode 100644 index 1ad582207a..0000000000 --- a/docs/feature_autocorrect.md +++ /dev/null @@ -1,324 +0,0 @@ -# Autocorrect - -There are a lot of words that are prone to being typed incorrectly, due to habit, sequence or just user error. This feature leverages your firmware to automatically correct these errors, to help reduce typos. - -## How does it work? {#how-does-it-work} - -The feature maintains a small buffer of recent key presses. On each key press, it checks whether the buffer ends in a recognized typo, and if so, automatically sends keystrokes to correct it. - -The tricky part is how to efficiently check the buffer for typos. We don’t want to spend too much memory or time on storing or searching the typos. A good solution is to represent the typos with a trie data structure. A trie is a tree data structure where each node is a letter, and words are formed by following a path to one of the leaves. - -![An example trie](https://i.imgur.com/HL5DP8H.png) - -Since we search whether the buffer ends in a typo, we store the trie writing in reverse. The trie is queried starting from the last letter, then second to last letter, and so on, until either a letter doesn’t match or we reach a leaf, meaning a typo was found. - -## How do I enable Autocorrection {#how-do-i-enable-autocorrection} - -In your `rules.mk`, add this: - -```make -AUTOCORRECT_ENABLE = yes -``` - -Additionally, you will need a library for autocorrection. A small sample library is included by default, so that you can get up and running right away, but you can provide a customized library. - -By default, autocorrect is disabled. To enable it, you need to use the `AC_TOGG` keycode to enable it. The status is stored in persistent memory, so you shouldn't need to enabled it again. - -## Customizing autocorrect library {#customizing-autocorrect-library} - -To provide a custom library, you need to create a text file with the corrections. For instance: - -```text -:thier -> their -fitler -> filter -lenght -> length -ouput -> output -widht -> width -``` - -The syntax is `typo -> correction`. Typos and corrections are case insensitive, and any whitespace before or after the typo and correction is ignored. The typo must be only the letters a–z, or the special character : representing a word break. The correction may have any non-unicode characters. - -Then, run: - -```sh -qmk generate-autocorrect-data autocorrect_dictionary.txt -``` - -This will process the file and produce an `autocorrect_data.h` file with the trie library, in the folder that you are at. You can specify the keyboard and keymap (eg `-kb planck/rev6 -km jackhumbert`), and it will place the file in that folder instead. But as long as the file is located in your keymap folder, or user folder, it should be picked up automatically. - -This file will look like this: - -```c -// :thier -> their -// fitler -> filter -// lenght -> length -// ouput -> output -// widht -> width - -#define AUTOCORRECT_MIN_LENGTH 5 // "ouput" -#define AUTOCORRECT_MAX_LENGTH 6 // ":thier" - -#define DICTIONARY_SIZE 74 - -static const uint8_t autocorrect_data[DICTIONARY_SIZE] PROGMEM = {85, 7, 0, 23, 35, 0, 0, 8, 0, 76, 16, 0, 15, 25, 0, 0, - 11, 23, 44, 0, 130, 101, 105, 114, 0, 23, 12, 9, 0, 131, 108, 116, 101, 114, 0, 75, 42, 0, 24, 64, 0, 0, 71, 49, 0, - 10, 56, 0, 0, 12, 26, 0, 129, 116, 104, 0, 17, 8, 15, 0, 129, 116, 104, 0, 19, 24, 18, 0, 130, 116, 112, 117, 116, - 0}; -``` - -### Avoiding false triggers {#avoiding-false-triggers} - -By default, typos are searched within words, to find typos within longer identifiers like maxFitlerOuput. While this is useful, a consequence is that autocorrection will falsely trigger when a typo happens to be a substring of a correctly-spelled word. For instance, if we had thier -> their as an entry, it would falsely trigger on (correct, though relatively uncommon) words like “wealthier” and “filthier.” - -The solution is to set a word break : before and/or after the typo to constrain matching. : matches space, period, comma, underscore, digits, and most other non-alpha characters. - -|Text |thier |:thier |thier: |:thier: | -|-----------------|:------:|:------:|:------:|:------:| -|see `thier` typo |matches |matches |matches |matches | -|it’s `thiers` |matches |matches |no |no | -|wealthier words |matches |no |matches |no | - -:thier: is most restrictive, matching only when thier is a whole word. - -The `qmk generate-autocorrect-data` commands can make an effort to check for entries that would false trigger as substrings of correct words. It searches each typo against a dictionary of 25K English words from the english_words Python package, provided it’s installed. (run `python3 -m pip install english_words` to install it.) - -::: tip -Unfortunately, this is limited to just english words, at this point. -::: - -## Overriding Autocorrect - -Occasionally you might actually want to type a typo (for instance, while editing autocorrect_dict.txt) without being autocorrected. There are a couple of ways to do this: - -1. Begin typing the typo. -2. Before typing the last letter, press and release the Ctrl or Alt key. -3. Type the remaining letters. - -This works because the autocorrection implementation doesn’t understand hotkeys, so it resets itself whenever a modifier other than shift is held. - -Additionally, you can use the `AC_TOGG` keycode to toggle the on/off status for Autocorrect. - -### Keycodes {#keycodes} - -|Keycode |Aliases |Description | -|-----------------------|---------|----------------------------------------------| -|`QK_AUTOCORRECT_ON` |`AC_ON` |Turns on the Autocorrect feature. | -|`QK_AUTOCORRECT_OFF` |`AC_OFF` |Turns off the Autocorrect feature. | -|`QK_AUTOCORRECT_TOGGLE`|`AC_TOGG`|Toggles the status of the Autocorrect feature.| - -## User Callback Functions - -### Process Autocorrect - -Callback function `bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods)` is available to customise incoming keycodes and handle exceptions. You can use this function to sanitise input before they are passed onto the autocorrect engine - -::: tip -Sanitisation of input is required because autocorrect will only match 8-bit [basic keycodes](keycodes_basic) for typos. If valid modifier keys or 16-bit keycodes that are part of a user's word input (such as Shift + A) is passed through, they will fail typo letter detection. For example a [Mod-Tap](mod_tap) key such as `LCTL_T(KC_A)` is 16-bit and should be masked for the 8-bit `KC_A`. -::: - -The default user callback function is found inside `quantum/process_keycode/process_autocorrect.c`. It covers most use-cases for QMK special functions and quantum keycodes, including [overriding autocorrect](#overriding-autocorrect) with a modifier other than shift. The `process_autocorrect_user` function is `weak` defined to allow user's copy inside `keymap.c` (or code files) to overwrite it. - -#### Process Autocorrect Example - -If you have a custom keycode `QMKBEST` that should be ignored as part of a word, and another custom keycode `QMKLAYER` that should override autocorrect, both can be added to the bottom of the `process_autocorrect_user` `switch` statement in your source code: - -```c -bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods) { - // See quantum_keycodes.h for reference on these matched ranges. - switch (*keycode) { - // Exclude these keycodes from processing. - case KC_LSFT: - case KC_RSFT: - case KC_CAPS: - case QK_TO ... QK_ONE_SHOT_LAYER_MAX: - case QK_LAYER_TAP_TOGGLE ... QK_LAYER_MOD_MAX: - case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX: - return false; - - // Mask for base keycode from shifted keys. - case QK_LSFT ... QK_LSFT + 255: - case QK_RSFT ... QK_RSFT + 255: - if (*keycode >= QK_LSFT && *keycode <= (QK_LSFT + 255)) { - *mods |= MOD_LSFT; - } else { - *mods |= MOD_RSFT; - } - *keycode &= 0xFF; // Get the basic keycode. - return true; -#ifndef NO_ACTION_TAPPING - // Exclude tap-hold keys when they are held down - // and mask for base keycode when they are tapped. - case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: -# ifdef NO_ACTION_LAYER - // Exclude Layer Tap, if layers are disabled - // but action tapping is still enabled. - return false; -# endif - case QK_MOD_TAP ... QK_MOD_TAP_MAX: - // Exclude hold if mods other than Shift is not active - if (!record->tap.count) { - return false; - } - *keycode &= 0xFF; - break; -#else - case QK_MOD_TAP ... QK_MOD_TAP_MAX: - case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: - // Exclude if disabled - return false; -#endif - // Exclude swap hands keys when they are held down - // and mask for base keycode when they are tapped. - case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX: -#ifdef SWAP_HANDS_ENABLE - if (*keycode >= 0x56F0 || !record->tap.count) { - return false; - } - *keycode &= 0xFF; - break; -#else - // Exclude if disabled - return false; -#endif - // Handle custom keycodes - case QMKBEST: - return false; - case QMKLAYER: - *typo_buffer_size = 0; - return false; - } - - // Disable autocorrect while a mod other than shift is active. - if ((*mods & ~MOD_MASK_SHIFT) != 0) { - *typo_buffer_size = 0; - return false; - } - - return true; -} -``` - -::: tip -In this callback function, `return false` will skip processing of that keycode for autocorrect. Adding `*typo_buffer_size = 0` will also reset the autocorrect buffer at the same time, cancelling any current letters already stored in the buffer. -::: - -### Apply Autocorrect - -Additionally, `apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct)` allows for users to add additional handling to the autocorrection, or replace the functionality entirely. This passes on the number of backspaces needed to replace the words, as well as the replacement string (partial word, not the full word), and the typo and corrected strings (complete words). - -::: tip -Due to the way code works (no notion of words, just a stream of letters), the `typo` and `correct` strings are a best bet and could be "wrong". For example you may get `wordtpyo` & `wordtypo` instead of the expected `tpyo` & `typo`. -::: - -#### Apply Autocorrect Example - -This following example will play a sound when a typo is autocorrected and execute the autocorrection itself: - -```c -#ifdef AUDIO_ENABLE -float autocorrect_song[][2] = SONG(TERMINAL_SOUND); -#endif - -bool apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct) { -#ifdef AUDIO_ENABLE - PLAY_SONG(autocorrect_song); -#endif - for (uint8_t i = 0; i < backspaces; ++i) { - tap_code(KC_BSPC); - } - send_string_P(str); - return false; -} -``` - -::: tip -In this callback function, `return false` will stop the normal processing of autocorrect, which requires manually handling of removing the "bad" characters and typing the new characters. -::: - -::: warning -***IMPORTANT***: `str` is a pointer to `PROGMEM` data for the autocorrection. If you return false, and want to send the string, this needs to use `send_string_P` and not `send_string` nor `SEND_STRING`. -::: - -You can also use `apply_autocorrect` to detect and display the event but allow internal code to execute the autocorrection with `return true`: - -```c -bool apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct) { -#ifdef OLED_ENABLE - oled_write_P(PSTR("Auto-corrected"), false); -#endif -#ifdef CONSOLE_ENABLE - printf("'%s' was corrected to '%s'\n", typo, correct); -#endif - return true; -} -``` - -### Autocorrect Status - -Additional user callback functions to manipulate Autocorrect: - -| Function | Description | -|----------------------------|----------------------------------------------| -| `autocorrect_enable()` | Turns Autocorrect on. | -| `autocorrect_disable()` | Turns Autocorrect off. | -| `autocorrect_toggle()` | Toggles Autocorrect. | -| `autocorrect_is_enabled()` | Returns true if Autocorrect is currently on. | - - -## Appendix: Trie binary data format {#appendix} - -This section details how the trie is serialized to byte data in autocorrect_data. You don’t need to care about this to use this autocorrection implementation. But it is documented for the record in case anyone is interested in modifying the implementation, or just curious how it works. - -What I did here is fairly arbitrary, but it is simple to decode and gets the job done. - -### Encoding {#encoding} - -All autocorrection data is stored in a single flat array autocorrect_data. Each trie node is associated with a byte offset into this array, where data for that node is encoded, beginning with root at offset 0. There are three kinds of nodes. The highest two bits of the first byte of the node indicate what kind: - -* 00 ⇒ chain node: a trie node with a single child. -* 01 ⇒ branching node: a trie node with multiple children. -* 10 ⇒ leaf node: a leaf, corresponding to a typo and storing its correction. - -![An example trie](https://i.imgur.com/HL5DP8H.png) - -**Branching node**. Each branch is encoded with one byte for the keycode (KC_A–KC_Z) followed by a link to the child node. Links between nodes are 16-bit byte offsets relative to the beginning of the array, serialized in little endian order. - -All branches are serialized this way, one after another, and terminated with a zero byte. As described above, the node is identified as a branch by setting the two high bits of the first byte to 01, done by bitwise ORing the first keycode with 64. keycode. The root node for the above figure would be serialized like: - -``` -+-------+-------+-------+-------+-------+-------+-------+ -| R|64 | node 2 | T | node 3 | 0 | -+-------+-------+-------+-------+-------+-------+-------+ -``` - -**Chain node**. Tries tend to have long chains of single-child nodes, as seen in the example above with f-i-t-l in fitler. So to save space, we use a different format to encode chains than branching nodes. A chain is encoded as a string of keycodes, beginning with the node closest to the root, and terminated with a zero byte. The child of the last node in the chain is encoded immediately after. That child could be either a branching node or a leaf. - -In the figure above, the f-i-t-l chain is encoded as - -``` -+-------+-------+-------+-------+-------+ -| L | T | I | F | 0 | -+-------+-------+-------+-------+-------+ -``` - -If we were to encode this chain using the same format used for branching nodes, we would encode a 16-bit node link with every node, costing 8 more bytes in this example. Across the whole trie, this adds up. Conveniently, we can point to intermediate points in the chain and interpret the bytes in the same way as before. E.g. starting at the i instead of the l, and the subchain has the same format. - -**Leaf node**. A leaf node corresponds to a particular typo and stores data to correct the typo. The leaf begins with a byte for the number of backspaces to type, and is followed by a null-terminated ASCII string of the replacement text. The idea is, after tapping backspace the indicated number of times, we can simply pass this string to the `send_string_P` function. For fitler, we need to tap backspace 3 times (not 4, because we catch the typo as the final ‘r’ is pressed) and replace it with lter. To identify the node as a leaf, the two high bits are set to 10 by ORing the backspace count with 128: - -``` -+-------+-------+-------+-------+-------+-------+ -| 3|128 | 'l' | 't' | 'e' | 'r' | 0 | -+-------+-------+-------+-------+-------+-------+ -``` - -### Decoding {#decoding} - -This format is by design decodable with fairly simple logic. A 16-bit variable state represents our current position in the trie, initialized with 0 to start at the root node. Then, for each keycode, test the highest two bits in the byte at state to identify the kind of node. - -* 00 ⇒ **chain node**: If the node’s byte matches the keycode, increment state by one to go to the next byte. If the next byte is zero, increment again to go to the following node. -* 01 ⇒ **branching node**: Search the branches for one that matches the keycode, and follow its node link. -* 10 ⇒ **leaf node**: a typo has been found! We read its first byte for the number of backspaces to type, then pass its following bytes to send_string_P to type the correction. - -## Credits - -Credit goes to [getreuer](https://github.com/getreuer) for originally implementing this [here](https://getreuer.info/posts/keyboards/autocorrection/#how-does-it-work). As well as to [filterpaper](https://github.com/filterpaper) for converting the code to use PROGMEM, and additional improvements. diff --git a/docs/feature_backlight.md b/docs/feature_backlight.md deleted file mode 100644 index 545d7be949..0000000000 --- a/docs/feature_backlight.md +++ /dev/null @@ -1,309 +0,0 @@ -# Backlighting {#backlighting} - -Many keyboards support backlit keys by way of individual LEDs placed through or underneath the keyswitches. This feature is distinct from both the [RGB Underglow](feature_rgblight) and [RGB Matrix](feature_rgb_matrix) features as it usually allows for only a single colour per switch, though you can obviously install multiple different single coloured LEDs on a keyboard. - -QMK is able to control the brightness of these LEDs by switching them on and off rapidly in a certain ratio, a technique known as *Pulse Width Modulation*, or PWM. By altering the duty cycle of the PWM signal, it creates the illusion of dimming. - -## Usage {#usage} - -Most keyboards have backlighting enabled by default if they support it, but if it is not working for you (or you have added support), check that your `rules.mk` includes the following: - -```make -BACKLIGHT_ENABLE = yes -``` - -## Keycodes {#keycodes} - -|Key |Aliases |Description | -|-------------------------------|---------|-----------------------------------| -|`QK_BACKLIGHT_TOGGLE` |`BL_TOGG`|Turn the backlight on or off | -|`QK_BACKLIGHT_STEP` |`BL_STEP`|Cycle through backlight levels | -|`QK_BACKLIGHT_ON` |`BL_ON` |Set the backlight to max brightness| -|`QK_BACKLIGHT_OFF` |`BL_OFF` |Turn the backlight off | -|`QK_BACKLIGHT_UP` |`BL_UP` |Increase the backlight level | -|`QK_BACKLIGHT_DOWN` |`BL_DOWN`|Decrease the backlight level | -|`QK_BACKLIGHT_TOGGLE_BREATHING`|`BL_BRTG`|Toggle backlight breathing | - -## Basic Configuration {#basic-configuration} - -Add the following to your `config.h`: - -|Define |Default |Description | -|-----------------------------|------------------|-----------------------------------------------------------------------------------------------------------------| -|`BACKLIGHT_PIN` |*Not defined* |The pin that controls the LEDs | -|`BACKLIGHT_LEVELS` |`3` |The number of brightness levels (maximum 31 excluding off) | -|`BACKLIGHT_CAPS_LOCK` |*Not defined* |Enable Caps Lock indicator using backlight (for keyboards without dedicated LED) | -|`BACKLIGHT_BREATHING` |*Not defined* |Enable backlight breathing, if supported | -|`BREATHING_PERIOD` |`6` |The length of one backlight "breath" in seconds | -|`BACKLIGHT_ON_STATE` |`1` |The state of the backlight pin when the backlight is "on" - `1` for high, `0` for low | -|`BACKLIGHT_LIMIT_VAL` |`255` |The maximum duty cycle of the backlight -- `255` allows for full brightness, any lower will decrease the maximum.| -|`BACKLIGHT_DEFAULT_ON` |`true` |Enable backlight upon clearing the EEPROM | -|`BACKLIGHT_DEFAULT_BREATHING`|`false` |Whether to enable backlight breathing upon clearing the EEPROM | -|`BACKLIGHT_DEFAULT_LEVEL` |`BACKLIGHT_LEVELS`|The default backlight level to use upon clearing the EEPROM | - -Unless you are designing your own keyboard, you generally should not need to change the `BACKLIGHT_PIN` or `BACKLIGHT_ON_STATE`. - -### "On" State {#on-state} - -Most backlight circuits are driven by an N-channel MOSFET or NPN transistor. This means that to turn the transistor *on* and light the LEDs, you must drive the backlight pin, connected to the gate or base, *high*. -Sometimes, however, a P-channel MOSFET, or a PNP transistor is used. In this case, when the transistor is on, the pin is driven *low* instead. - -To configure the "on" state of the backlight circuit, add the following to your `config.h`: - -```c -#define BACKLIGHT_ON_STATE 0 -``` - -### Multiple Backlight Pins {#multiple-backlight-pins} - -Most keyboards have only one backlight pin which controls all backlight LEDs (especially if the backlight is connected to a hardware PWM pin). -The `timer` and `software` drivers allow you to define multiple backlight pins, which will be turned on and off at the same time during the PWM duty cycle. - -This feature allows to set, for instance, the Caps Lock LED's (or any other controllable LED) brightness at the same level as the other LEDs of the backlight. This is useful if you have mapped Control in place of Caps Lock and you need the Caps Lock LED to be part of the backlight instead of being activated when Caps Lock is on, as it is usually wired to a separate pin from the backlight. - -To configure multiple backlight pins, add something like this to your `config.h`, instead of `BACKLIGHT_PIN`: - -```c -#define BACKLIGHT_PINS { F5, B2 } -``` - -## Driver Configuration {#driver-configuration} - -Backlight driver selection is configured in `rules.mk`. Valid drivers are `pwm` (default), `timer`, `software`, or `custom`. See below for information on individual drivers. - -### PWM Driver {#pwm-driver} - -This is the default backlight driver, which leverages the hardware PWM output capability of the microcontroller. - -```make -BACKLIGHT_DRIVER = pwm -``` - -### Timer Driver {#timer-driver} - -This driver is similar to the PWM driver, but instead of directly configuring the pin to output a PWM signal, an interrupt handler is attached to the timer to turn the pin on and off as appropriate. - -```make -BACKLIGHT_DRIVER = timer -``` - -### Software Driver {#software-driver} - -In this mode, PWM is "emulated" while running other keyboard tasks. It offers maximum hardware compatibility without extra platform configuration. However, breathing is not supported, and the backlight can flicker when the keyboard is busy. - -```make -BACKLIGHT_DRIVER = software -``` - -### Custom Driver {#custom-driver} - -If none of the above drivers apply to your board (for example, you are using a separate IC to control the backlight), you can implement a custom backlight driver using a simple API. - -```make -BACKLIGHT_DRIVER = custom -``` - -```c -void backlight_init_ports(void) { - // Optional - runs on startup - // Usually you want to configure pins here -} -void backlight_set(uint8_t level) { - // Optional - runs on level change - // Usually you want to respond to the new value -} - -void backlight_task(void) { - // Optional - runs periodically - // Note that this is called in the main keyboard loop, - // so long running actions here can cause performance issues -} -``` - -## AVR Configuration {#avr-configuration} - -### PWM Driver {#avr-pwm-driver} - -The following table describes the supported pins for the PWM driver. Only cells marked with a timer number are capable of hardware PWM output; any others must use the `timer` driver. - -|Backlight Pin|AT90USB64/128|AT90USB162|ATmega16/32U4|ATmega16/32U2|ATmega32A|ATmega328/P| -|-------------|-------------|----------|-------------|-------------|---------|-----------| -|`B1` | | | | | |Timer 1 | -|`B2` | | | | | |Timer 1 | -|`B5` |Timer 1 | |Timer 1 | | | | -|`B6` |Timer 1 | |Timer 1 | | | | -|`B7` |Timer 1 |Timer 1 |Timer 1 |Timer 1 | | | -|`C4` |Timer 3 | | | | | | -|`C5` |Timer 3 |Timer 1 | |Timer 1 | | | -|`C6` |Timer 3 |Timer 1 |Timer 3 |Timer 1 | | | -|`D4` | | | | |Timer 1 | | -|`D5` | | | | |Timer 1 | | - -### Timer Driver {#avr-timer-driver} - -Any GPIO pin can be used with this driver. The following table describes the supported timers: - -|AT90USB64/128|AT90USB162|ATmega16/32U4|ATmega16/32U2|ATmega32A|ATmega328/P| -|-------------|----------|-------------|-------------|---------|-----------| -|Timers 1 & 3 |Timer 1 |Timers 1 & 3 |Timer 1 |Timer 1 |Timer 1 | - -The following `#define`s apply only to the `timer` driver: - -|Define |Default|Description | -|-----------------------|-------|----------------| -|`BACKLIGHT_PWM_TIMER` |`1` |The timer to use| - -Note that the choice of timer may conflict with the [Audio](feature_audio) feature. - -## ChibiOS/ARM Configuration {#arm-configuration} - -### PWM Driver {#arm-pwm-driver} - -Depending on the ChibiOS board configuration, you may need to enable PWM at the keyboard level. For STM32, this would look like: - -`halconf.h`: -```c -#define HAL_USE_PWM TRUE -``` -`mcuconf.h`: -```c -#undef STM32_PWM_USE_TIM4 -#define STM32_PWM_USE_TIM4 TRUE -``` - -The following `#define`s apply only to the `pwm` driver: - -|Define |Default |Description | -|-----------------------|-------------|---------------------------------------------------------------| -|`BACKLIGHT_PWM_DRIVER` |`PWMD4` |The PWM driver to use | -|`BACKLIGHT_PWM_CHANNEL`|`3` |The PWM channel to use | -|`BACKLIGHT_PAL_MODE` |`2` |The pin alternative function to use | -|`BACKLIGHT_PWM_PERIOD` |*Not defined*|The PWM period in counter ticks - Default is platform dependent| - - -Refer to the ST datasheet for your particular MCU to determine these values. For example, these defaults are set up for pin `B8` on a Proton-C (STM32F303) using `TIM4_CH3` on AF2. Unless you are designing your own keyboard, you generally should not need to change them. - -### Timer Driver {#arm-timer-driver} - -Depending on the ChibiOS board configuration, you may need to enable general-purpose timers at the keyboard level. For STM32, this would look like: - -`halconf.h`: -```c -#define HAL_USE_GPT TRUE -``` -`mcuconf.h`: -```c -#undef STM32_GPT_USE_TIM15 -#define STM32_GPT_USE_TIM15 TRUE -``` - -The following `#define`s apply only to the `timer` driver: - -|Define |Default |Description | -|----------------------|--------|----------------| -|`BACKLIGHT_GPT_DRIVER`|`GPTD15`|The timer to use| - -## Example Schematic - -Since the MCU can only supply so much current to its GPIO pins, instead of powering the backlight directly from the MCU, the backlight pin is connected to a transistor or MOSFET that switches the power to the LEDs. - -In this typical example, the backlight LEDs are all connected in parallel towards an N-channel MOSFET. Its gate pin is wired to one of the microcontroller's GPIO pins through a 470Ω resistor to avoid ringing. -A pulldown resistor is also placed between the gate pin and ground to keep it at a defined state when it is not otherwise being driven by the MCU. -The values of these resistors are not critical - see [this Electronics StackExchange question](https://electronics.stackexchange.com/q/68748) for more information. - -![Backlight example circuit](https://i.imgur.com/BmAvoUC.png) - -## API {#api} - -### `void backlight_toggle(void)` {#api-backlight-toggle} - -Toggle the backlight on or off. - ---- - -### `void backlight_enable(void)` {#api-backlight-enable} - -Turn the backlight on. - ---- - -### `void backlight_disable(void)` {#api-backlight-disable} - -Turn the backlight off. - ---- - -### `void backlight_step(void)` {#api-backlight-step} - -Cycle through backlight levels. - ---- - -### `void backlight_increase(void)` {#api-backlight-increase} - -Increase the backlight level. - ---- - -### `void backlight_decrease(void)` {#api-backlight-decrease} - -Decrease the backlight level. - ---- - -### `void backlight_level(uint8_t level)` {#api-backlight-level} - -Set the backlight level. - -#### Arguments {#api-backlight-level-arguments} - - - `uint8_t level` - The level to set, from 0 to `BACKLIGHT_LEVELS`. - ---- - -### `uint8_t get_backlight_level(void)` {#api-get-backlight-level} - -Get the current backlight level. - -#### Return Value {#api-get-backlight-level-return} - -The current backlight level, from 0 to `BACKLIGHT_LEVELS`. - ---- - -### `bool is_backlight_enabled(void)` {#api-is-backlight-enabled} - -Get the current backlight state. - -#### Return Value {#api-is-backlight-enabled-return} - -`true` if the backlight is enabled. - ---- - -### `void backlight_toggle_breathing(void)` {#api-backlight-toggle-breathing} - -Toggle backlight breathing on or off. - ---- - -### `void backlight_enable_breathing(void)` {#api-backlight-enable-breathing} - -Turn backlight breathing on. - ---- - -### `void backlight_disable_breathing(void)` {#api-backlight-disable-breathing} - -Turn backlight breathing off. - ---- - -### `bool is_backlight_breathing(void)` {#api-is-backlight-breathing} - -Get the current backlight breathing state. - -#### Return Value {#api-is-backlight-breathing-return} - -`true` if backlight breathing is enabled. diff --git a/docs/feature_bluetooth.md b/docs/feature_bluetooth.md deleted file mode 100644 index 7562a75447..0000000000 --- a/docs/feature_bluetooth.md +++ /dev/null @@ -1,46 +0,0 @@ -# Bluetooth - -## Bluetooth Known Supported Hardware - -Currently Bluetooth support is limited to AVR based chips. For Bluetooth 2.1, QMK has support for RN-42 modules. For more recent BLE protocols, currently only the Adafruit Bluefruit SPI Friend is directly supported. BLE is needed to connect to iOS devices. Note iOS does not support mouse input. - -|Board |Bluetooth Protocol |Connection Type|rules.mk |Bluetooth Chip| -|----------------------------------------------------------------|--------------------|---------------|---------------------------------|--------------| -|Roving Networks RN-42 (Sparkfun Bluesmirf) |Bluetooth Classic |UART |`BLUETOOTH_DRIVER = rn42` |RN-42 | -|[Bluefruit LE SPI Friend](https://www.adafruit.com/product/2633)|Bluetooth Low Energy|SPI |`BLUETOOTH_DRIVER = bluefruit_le`|nRF51822 | - -Not Supported Yet but possible: -* [Bluefruit LE UART Friend](https://www.adafruit.com/product/2479). [Possible tmk implementation found in](https://github.com/tmk/tmk_keyboard/issues/514) -* HC-05 boards flashed with RN-42 firmware. They apparently both use the CSR BC417 Chip. Flashing it with RN-42 firmware gives it HID capability. -* Sparkfun Bluetooth Mate -* HM-13 based boards - -### Adafruit BLE SPI Friend -Currently The only bluetooth chipset supported by QMK is the Adafruit Bluefruit SPI Friend. It's a Nordic nRF51822 based chip running Adafruit's custom firmware. Data is transmitted via Adafruit's SDEP over Hardware SPI. The [Feather 32u4 Bluefruit LE](https://www.adafruit.com/product/2829) is supported as it's an AVR mcu connected via SPI to the Nordic BLE chip with Adafruit firmware. If Building a custom board with the SPI friend it would be easiest to just use the pin selection that the 32u4 feather uses but you can change the pins in the config.h options with the following defines: -* `#define BLUEFRUIT_LE_RST_PIN D4` -* `#define BLUEFRUIT_LE_CS_PIN B4` -* `#define BLUEFRUIT_LE_IRQ_PIN E6` - -A Bluefruit UART friend can be converted to an SPI friend, however this [requires](https://github.com/qmk/qmk_firmware/issues/2274) some reflashing and soldering directly to the MDBT40 chip. - - -## Bluetooth Rules.mk Options - -The currently supported Bluetooth chipsets do not support [N-Key Rollover (NKRO)](reference_glossary#n-key-rollover-nkro), so `rules.mk` must contain `NKRO_ENABLE = no`. - -Add the following to your `rules.mk`: - -```make -BLUETOOTH_ENABLE = yes -BLUETOOTH_DRIVER = bluefruit_le # or rn42 -``` - -## Bluetooth Keycodes - -This is used when multiple keyboard outputs can be selected. Currently this only allows for switching between USB and Bluetooth on keyboards that support both. - -|Key |Aliases |Description | -|---------------------|---------|----------------------------------------------| -|`QK_OUTPUT_AUTO` |`OU_AUTO`|Automatically switch between USB and Bluetooth| -|`QK_OUTPUT_USB` |`OU_USB` |USB only | -|`QK_OUTPUT_BLUETOOTH`|`OU_BT` |Bluetooth only | diff --git a/docs/feature_bootmagic.md b/docs/feature_bootmagic.md deleted file mode 100644 index df0344ee8e..0000000000 --- a/docs/feature_bootmagic.md +++ /dev/null @@ -1,85 +0,0 @@ -# Bootmagic {#bootmagic} - -The Bootmagic feature that only handles jumping into the bootloader. This is great for boards that don't have a physical reset button, giving you a way to jump into the bootloader - -On some keyboards Bootmagic is disabled by default. If this is the case, it must be explicitly enabled in your `rules.mk` with: - -```make -BOOTMAGIC_ENABLE = yes -``` - -Additionally, you may want to specify which key to use. This is especially useful for keyboards that have unusual matrices. To do so, you need to specify the row and column of the key that you want to use. Add these entries to your `config.h` file: - -```c -#define BOOTMAGIC_ROW 0 -#define BOOTMAGIC_COLUMN 1 -``` - -By default, these are set to 0 and 0, which is usually the "ESC" key on a majority of keyboards. - -And to trigger the bootloader, you hold this key down when plugging the keyboard in. Just the single key. - -::: warning -Using Bootmagic will **always reset** the EEPROM, so you will lose any settings that have been saved. -::: - -## Split Keyboards - -When [handedness](feature_split_keyboard#setting-handedness) is predetermined via options like `SPLIT_HAND_PIN` or `EE_HANDS`, you might need to configure a different key between halves. To identify the correct key for the right half, examine the split key matrix defined in the `.h` file, e.g.: - -```c -#define LAYOUT_split_3x5_2( \ - L01, L02, L03, L04, L05, R01, R02, R03, R04, R05, \ - L06, L07, L08, L09, L10, R06, R07, R08, R09, R10, \ - L11, L12, L13, L14, L15, R11, R12, R13, R14, R15, \ - L16, L17, R16, R17 \ - ) \ - { \ - { L01, L02, L03, L04, L05 }, \ - { L06, L07, L08, L09, L10 }, \ - { L11, L12, L13, L14, L15 }, \ - { L16, L17, KC_NO, KC_NO, KC_NO }, \ - { R01, R02, R03, R04, R05 }, \ - { R06, R07, R08, R09, R10 }, \ - { R11, R12, R13, R14, R15 }, \ - { R16, R17, KC_NO, KC_NO, KC_NO } \ - } -``` - -If you pick the top right key for the right half, it is `R05` on the top layout. Within the key matrix below, `R05` is located on row 4 columnn 4. To use that key as the right half's Bootmagic trigger, add these entries to your `config.h` file: - -```c -#define BOOTMAGIC_ROW_RIGHT 4 -#define BOOTMAGIC_COLUMN_RIGHT 4 -``` - -::: tip -These values are not set by default. -::: - -## Advanced Bootmagic - -The `bootmagic_scan` function is defined weakly, so that you can replace this in your code, if you need. A great example of this is the Zeal60 boards that have some additional handling needed. - -To replace the function, all you need to do is add something like this to your code: - -```c -void bootmagic_scan(void) { - matrix_scan(); - wait_ms(DEBOUNCE * 2); - matrix_scan(); - - if (matrix_get_row(BOOTMAGIC_ROW) & (1 << BOOTMAGIC_COLUMN)) { - // Jump to bootloader. - bootloader_jump(); - } -} -``` - -You can define additional logic here. For instance, resetting the EEPROM or requiring additional keys to be pressed to trigger Bootmagic. Keep in mind that `bootmagic_scan` is called before a majority of features are initialized in the firmware. - -## Addenda - -To manipulate settings that were formerly configured through the now-deprecated full Bootmagic feature, see [Magic Keycodes](keycodes_magic). - -The Command feature, formerly known as Magic, also allows you to control different aspects of your keyboard. While it shares some functionality with Magic Keycodes, it also allows you to do things that Magic Keycodes cannot, such as printing version information to the console. For more information, see [Command](feature_command). diff --git a/docs/feature_caps_word.md b/docs/feature_caps_word.md deleted file mode 100644 index 666edecb6e..0000000000 --- a/docs/feature_caps_word.md +++ /dev/null @@ -1,189 +0,0 @@ -# Caps Word - -It is often useful to type a single word in all capitals, for instance -abbreviations like "QMK", or in code, identifiers like `KC_SPC`. "Caps Word" is -a modern alternative to Caps Lock: - -* While active, letters are capitalized and `-` becomes `_`. The `_` makes it easier - to type constant names (eg 'PROGRAM\_CONSTANTS'). - -* Caps Word automatically disables - itself at the end of the word. That is, it stops by default once a space or - any key other than `KC_A`--`KC_Z`, `KC_0`--`KC_9`, `KC_MINS`, `KC_UNDS`, - `KC_DELETE`, or `KC_BACKSPACE` is pressed. Caps Word also disables itself if - the keyboard is idle for 5 seconds. This is configurable, see below. - -* To avoid requiring a dedicated key for Caps Word, there is an option - (`BOTH_SHIFTS_TURNS_ON_CAPS_WORD`) to activate Caps Word by simultaneously - pressing both shift keys. See below for other options. - -* The implementation does not use the Caps Lock (`KC_CAPS`) keycode. Caps Word - works even if you're remapping Caps Lock at the OS level to Ctrl or something - else, as Emacs and Vim users often do. As a consequence, Caps Word does not - follow the typical Caps Lock behaviour and may thus act in potentially - unexpected ways, especially when using an *OS* keyboard layout other than US - or UK. For example, Dvorak's , < key (`DV_COMM` aka `KC_W`) will - get shifted because Caps Word interprets that keycode as the letter 'W' by - default, the Spanish Ñ key (`ES_NTIL` aka `KC_SCLN`) will not get - capitalized because Caps Word interprets it as the semicolon ';' punctuation - character, and the US hyphen key (`KC_MINS`), while unaffected by Caps Lock, - is shifted by Caps Word. However, this is not really a problem because you can - [configure which keys should Caps Word - shift](#configure-which-keys-are-word-breaking). - - -## How do I enable Caps Word {#how-do-i-enable-caps-word} - -In your `rules.mk`, add: - -```make -CAPS_WORD_ENABLE = yes -``` - -Next, use one the following methods to activate Caps Word: - -* **Activate by pressing a key**: Use the `QK_CAPS_WORD_TOGGLE` keycode (short - alias `CW_TOGG`) in your keymap. - -* **Activate by pressing Left Shift + Right Shift**: Add `#define - BOTH_SHIFTS_TURNS_ON_CAPS_WORD` to config.h. You may also need to disable or - reconfigure Command, details below. Then, simultaneously pressing both left - and right shifts turns on Caps Word. This method works with the plain - `KC_LSFT` and `KC_RSFT` keycodes as well as one-shot shifts and Space Cadet - shifts. If your shift keys are mod-taps, hold both shift mod-tap keys until - the tapping term, then release them. - -* **Activate by double tapping Left Shift**: Add `#define - DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD` config.h. Then, double tapping Left Shift - turns on Caps Word. This method works with `KC_LSFT` or one-shot Left Shift - `OSM(MOD_LSFT)`. To count as a double tap, the maximum time in milliseconds - between taps is `TAPPING_TERM`, or if using `TAPPING_TERM_PER_KEY`, the time - returned by `get_tapping_term()` for the shift keycode being tapped. - -* **Custom activation**: You can activate Caps Word from code by calling - `caps_word_on()`. This may be used to activate Caps Word through [a - combo](feature_combo) or [tap dance](feature_tap_dance) or any means - you like. - -### Troubleshooting: Command {#troubleshooting-command} - -When using `BOTH_SHIFTS_TURNS_ON_CAPS_WORD`, you might see a compile message -**"BOTH_SHIFTS_TURNS_ON_CAPS_WORD and Command should not be enabled at the same -time, since both use the Left Shift + Right Shift key combination."** - -Many keyboards enable the [Command feature](feature_command), which by -default is also activated using the Left Shift + Right Shift key combination. To -fix this conflict, please disable Command by adding in rules.mk: - -```make -COMMAND_ENABLE = no -``` - -Or configure Command to use another key combination like Left Ctrl + Right Ctrl -by defining `IS_COMMAND()` in config.h: - -```c -// Activate Command with Left Ctrl + Right Ctrl. -#define IS_COMMAND() (get_mods() == MOD_MASK_CTRL) -``` - - -## Customizing Caps Word {#customizing-caps-word} - -### Invert on shift {#invert-on-shift} - -By default, Caps Word turns off when Shift keys are pressed, considering them as -word-breaking. Alternatively with the `CAPS_WORD_INVERT_ON_SHIFT` option, -pressing the Shift key continues Caps Word and inverts the shift state. This -is convenient for uncapitalizing one or a few letters within a word, for -example with Caps Word on, typing "D, B, Shift+A, Shift+A, S" produces "DBaaS", -or typing "P, D, F, Shift+S" produces "PDFs". - -Enable it by adding in config.h - -```c -#define CAPS_WORD_INVERT_ON_SHIFT -``` - -This option works with regular Shift keys `KC_LSFT` and `KC_RSFT`, mod-tap Shift -keys, and one-shot Shift keys. Note that while Caps Word is on, one-shot Shift -keys behave like regular Shift keys, and have effect only while they are held. - - -### Idle timeout {#idle-timeout} - -Caps Word turns off automatically if no keys are pressed for -`CAPS_WORD_IDLE_TIMEOUT` milliseconds. The default is 5000 (5 seconds). -Configure the timeout duration in config.h, for instance - -```c -#define CAPS_WORD_IDLE_TIMEOUT 3000 // 3 seconds. -``` - -Setting `CAPS_WORD_IDLE_TIMEOUT` to 0 configures Caps Word to never time out. -Caps Word then remains active indefinitely until a word breaking key is pressed. - - -### Functions {#functions} - -Functions to manipulate Caps Word: - -| Function | Description | -|-------------------------|------------------------------------------------| -| `caps_word_on()` | Turns Caps Word on. | -| `caps_word_off()` | Turns Caps Word off. | -| `caps_word_toggle()` | Toggles Caps Word. | -| `is_caps_word_on()` | Returns true if Caps Word is currently on. | - - -### Configure which keys are "word breaking" {#configure-which-keys-are-word-breaking} - -You can define the `caps_word_press_user(uint16_t keycode)` callback to -configure which keys should be shifted and which keys are considered "word -breaking" and stop Caps Word. - -The callback is called on every key press while Caps Word is active. When the -key should be shifted (that is, a letter key), the callback should call -`add_weak_mods(MOD_BIT(KC_LSFT))` to shift the key. Returning true continues the -current "word," while returning false is "word breaking" and deactivates Caps -Word. The default callback is - -```c -bool caps_word_press_user(uint16_t keycode) { - switch (keycode) { - // Keycodes that continue Caps Word, with shift applied. - case KC_A ... KC_Z: - case KC_MINS: - add_weak_mods(MOD_BIT(KC_LSFT)); // Apply shift to next key. - return true; - - // Keycodes that continue Caps Word, without shifting. - case KC_1 ... KC_0: - case KC_BSPC: - case KC_DEL: - case KC_UNDS: - return true; - - default: - return false; // Deactivate Caps Word. - } -} -``` - - -### Representing Caps Word state {#representing-caps-word-state} - -Define `caps_word_set_user(bool active)` to get callbacks when Caps Word turns -on or off. This is useful to represent the current Caps Word state, e.g. by -setting an LED or playing a sound. In your keymap, define - -```c -void caps_word_set_user(bool active) { - if (active) { - // Do something when Caps Word activates. - } else { - // Do something when Caps Word deactivates. - } -} -``` - diff --git a/docs/feature_combo.md b/docs/feature_combo.md deleted file mode 100644 index b49a444804..0000000000 --- a/docs/feature_combo.md +++ /dev/null @@ -1,398 +0,0 @@ -# Combos - -The Combo feature is a chording type solution for adding custom actions. It lets you hit multiple keys at once and produce a different effect. For instance, hitting `A` and `B` within the combo term would hit `ESC` instead, or have it perform even more complex tasks. - -To enable this feature, you need to add `COMBO_ENABLE = yes` to your `rules.mk`. - -Then, in your `keymap.c` file, you'll need to define a sequence of keys, terminated with `COMBO_END`, and a structure to list the combination of keys, and its resulting action. - -```c -const uint16_t PROGMEM test_combo1[] = {KC_A, KC_B, COMBO_END}; -const uint16_t PROGMEM test_combo2[] = {KC_C, KC_D, COMBO_END}; -combo_t key_combos[] = { - COMBO(test_combo1, KC_ESC), - COMBO(test_combo2, LCTL(KC_Z)), // keycodes with modifiers are possible too! -}; -``` - -This will send "Escape" if you hit the A and B keys, and Ctrl+Z when you hit the C and D keys. - -## Advanced Keycodes Support -Advanced keycodes, such as [Mod-Tap](mod_tap) and [Tap Dance](feature_tap_dance) are also supported together with combos. If you use these advanced keycodes in your keymap, you will need to place the full keycode in the combo definition, e.g.: - -```c -const uint16_t PROGMEM test_combo1[] = {LSFT_T(KC_A), LT(1, KC_B), COMBO_END}; -const uint16_t PROGMEM test_combo2[] = {TD(TD_ESC_CAPS), KC_F1, COMBO_END}; -``` - -## Overlapping Combos -It is possible to overlap combos. Before, with the example below both combos would activate when all three keys were pressed. Now only the three key combo will activate. - -```c -const uint16_t PROGMEM test_combo1[] = {LSFT_T(KC_A), LT(1, KC_B), COMBO_END}; -const uint16_t PROGMEM test_combo2[] = {LSFT_T(KC_A), LT(1, KC_B), KC_C, COMBO_END}; -combo_t key_combos[] = { - COMBO(test_combo1, KC_ESC) - COMBO(test_combo2, KC_TAB) -}; -``` - -## Examples - -A long list of combos can be defined in an `enum` list: - -```c -enum combos { - AB_ESC, - JK_TAB, - QW_SFT, - SD_LAYER -}; - -const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END}; -const uint16_t PROGMEM jk_combo[] = {KC_J, KC_K, COMBO_END}; -const uint16_t PROGMEM qw_combo[] = {KC_Q, KC_W, COMBO_END}; -const uint16_t PROGMEM sd_combo[] = {KC_S, KC_D, COMBO_END}; - -combo_t key_combos[] = { - [AB_ESC] = COMBO(ab_combo, KC_ESC), - [JK_TAB] = COMBO(jk_combo, KC_TAB), - [QW_SFT] = COMBO(qw_combo, KC_LSFT), - [SD_LAYER] = COMBO(sd_combo, MO(_LAYER)), -}; -``` - -For a more complicated implementation, you can use the `process_combo_event` function to add custom handling. - -```c -enum combo_events { - EM_EMAIL, - BSPC_LSFT_CLEAR, -}; - -const uint16_t PROGMEM email_combo[] = {KC_E, KC_M, COMBO_END}; -const uint16_t PROGMEM clear_line_combo[] = {KC_BSPC, KC_LSFT, COMBO_END}; - -combo_t key_combos[] = { - [EM_EMAIL] = COMBO_ACTION(email_combo), - [BSPC_LSFT_CLEAR] = COMBO_ACTION(clear_line_combo), -}; -/* COMBO_ACTION(x) is same as COMBO(x, KC_NO) */ - -void process_combo_event(uint16_t combo_index, bool pressed) { - switch(combo_index) { - case EM_EMAIL: - if (pressed) { - SEND_STRING("john.doe@example.com"); - } - break; - case BSPC_LSFT_CLEAR: - if (pressed) { - tap_code16(KC_END); - tap_code16(S(KC_HOME)); - tap_code16(KC_BSPC); - } - break; - } -} -``` - -This will send "john.doe@example.com" if you chord E and M together, and clear the current line with Backspace and Left-Shift. You could change this to do stuff like play sounds or change settings. - -It is worth noting that `COMBO_ACTION`s are not needed anymore. As of [PR#8591](https://github.com/qmk/qmk_firmware/pull/8591/), it is possible to run your own custom keycodes from combos. Just define the custom keycode, program its functionality in `process_record_user`, and define a combo with `COMBO(, )`. See the first example in [Macros](feature_macros). - -## Keycodes -You can enable, disable and toggle the Combo feature on the fly. This is useful if you need to disable them temporarily, such as for a game. The following keycodes are available for use in your `keymap.c` - -|Keycode |Aliases |Description | -|-----------------|---------|--------------------------------| -|`QK_COMBO_ON` |`CM_ON` |Turns on Combo feature | -|`QK_COMBO_OFF` |`CM_OFF` |Turns off Combo feature | -|`QK_COMBO_TOGGLE`|`CM_TOGG`|Toggles Combo feature on and off| - -## Advanced Configuration -These configuration settings can be set in your `config.h` file. - -### Combo Term -By default, the timeout for the Combos to be recognized is set to 50ms. This can be changed if accidental combo misfires are happening or if you're having difficulties pressing keys at the same time. For instance, `#define COMBO_TERM 40` would set the timeout period for combos to 40ms. - -### Buffer and state sizes -If you're using long combos, or you have a lot of overlapping combos, you may run into issues with this, as the buffers may not be large enough to accommodate what you're doing. In this case, you can configure the sizes of the buffers used. Be aware, larger combo sizes and larger buffers will increase memory usage! - -To configure the amount of keys a combo can be composed of, change the following: - -| Keys | Define to be set | -|------|-----------------------------------| -| 6 | `#define EXTRA_SHORT_COMBOS` | -| 8 | QMK Default | -| 16 | `#define EXTRA_LONG_COMBOS` | -| 32 | `#define EXTRA_EXTRA_LONG_COMBOS` | - -Defining `EXTRA_SHORT_COMBOS` combines a combo's internal state into just one byte. This can, in some cases, save some memory. If it doesn't, no point using it. If you do, you also have to make sure you don't define combos with more than 6 keys. - -Processing combos has two buffers, one for the key presses, another for the combos being activated. Use the following options to configure the sizes of these buffers: - -| Define | Default | -|-------------------------------------|------------------------------------------------------| -| `#define COMBO_KEY_BUFFER_LENGTH 8` | 8 (the key amount `(EXTRA_)EXTRA_LONG_COMBOS` gives) | -| `#define COMBO_BUFFER_LENGTH 4` | 4 | - -### Modifier Combos -If a combo resolves to a Modifier, the window for processing the combo can be extended independently from normal combos. By default, this is disabled but can be enabled with `#define COMBO_MUST_HOLD_MODS`, and the time window can be configured with `#define COMBO_HOLD_TERM 150` (default: `TAPPING_TERM`). With `COMBO_MUST_HOLD_MODS`, you cannot tap the combo any more which makes the combo less prone to misfires. - -### Strict key press order -By defining `COMBO_MUST_PRESS_IN_ORDER` combos only activate when the keys are pressed in the same order as they are defined in the key array. - -### Per Combo Timing, Holding, Tapping and Key Press Order -For each combo, it is possible to configure the time window it has to pressed in, if it needs to be held down, if it needs to be tapped, or if its keys need to be pressed in order. - -For example, tap-only combos are useful if any (or all) of the underlying keys are mod-tap or layer-tap keys. When you tap the combo, you get the combo result. When you press the combo and hold it down, the combo doesn't activate. Instead the keys are processed separately as if the combo wasn't even there. - -In order to use these features, the following configuration options and functions need to be defined. Coming up with useful timings and configuration is left as an exercise for the reader. - -| Config Flag | Function | Description | -|-----------------------------|-----------------------------------------------------------|--------------------------------------------------------------------------------------------------------| -| `COMBO_TERM_PER_COMBO` | uint16_t get_combo_term(uint16_t index, combo_t \*combo) | Optional per-combo timeout window. (default: `COMBO_TERM`) | -| `COMBO_MUST_HOLD_PER_COMBO` | bool get_combo_must_hold(uint16_t index, combo_t \*combo) | Controls if a given combo should fire immediately on tap or if it needs to be held. (default: `false`) | -| `COMBO_MUST_TAP_PER_COMBO` | bool get_combo_must_tap(uint16_t index, combo_t \*combo) | Controls if a given combo should fire only if tapped within `COMBO_HOLD_TERM`. (default: `false`) | -| `COMBO_MUST_PRESS_IN_ORDER_PER_COMBO` | bool get_combo_must_press_in_order(uint16_t index, combo_t \*combo) | Controls if a given combo should fire only if its keys are pressed in order. (default: `true`) | - -Examples: -```c -uint16_t get_combo_term(uint16_t index, combo_t *combo) { - // decide by combo->keycode - switch (combo->keycode) { - case KC_X: - return 50; - } - - // or with combo index, i.e. its name from enum. - switch (index) { - case COMBO_NAME_HERE: - return 9001; - } - - // And if you're feeling adventurous, you can even decide by the keys in the chord, - // i.e. the exact array of keys you defined for the combo. - // This can be useful if your combos have a common key and you want to apply the - // same combo term for all of them. - if (combo->keys[0] == KC_ENT) { // if first key in the array is Enter - return 150; - } - - return COMBO_TERM; -} - -bool get_combo_must_hold(uint16_t index, combo_t *combo) { - // Same as above, decide by keycode, the combo index, or by the keys in the chord. - - if (KEYCODE_IS_MOD(combo->keycode) || - (combo->keycode >= QK_MOMENTARY && combo->keycode <= QK_MOMENTARY_MAX) // MO(kc) keycodes - ) { - return true; - } - - switch (index) { - case COMBO_NAME_HERE: - return true; - } - - return false; -} - -bool get_combo_must_tap(uint16_t index, combo_t *combo) { - // If you want all combos to be tap-only, just uncomment the next line - // return true - - // If you want *all* combos, that have Mod-Tap/Layer-Tap/Momentary keys in its chord, to be tap-only, this is for you: - uint16_t key; - uint8_t idx = 0; - while ((key = pgm_read_word(&combo->keys[idx])) != COMBO_END) { - switch (key) { - case QK_MOD_TAP...QK_MOD_TAP_MAX: - case QK_LAYER_TAP...QK_LAYER_TAP_MAX: - case QK_MOMENTARY...QK_MOMENTARY_MAX: - return true; - } - idx += 1; - } - return false; - -} - -bool get_combo_must_press_in_order(uint16_t combo_index, combo_t *combo) { - switch (combo_index) { - /* List combos here that you want to only activate if their keys - * are pressed in the same order as they are defined in the combo's key - * array. */ - case COMBO_NAME_HERE: - return true; - default: - return false; - } -} -``` - -### Generic hook to (dis)allow a combo activation - -By defining `COMBO_SHOULD_TRIGGER` and its companying function `bool combo_should_trigger(uint16_t combo_index, combo_t *combo, uint16_t keycode, keyrecord_t *record)` you can block or allow combos to activate on the conditions of your choice. -For example, you could disallow some combos on the base layer and allow them on another. Or disable combos on the home row when a timer is running. - -Examples: -```c -bool combo_should_trigger(uint16_t combo_index, combo_t *combo, uint16_t keycode, keyrecord_t *record) { - /* Disable combo `SOME_COMBO` on layer `_LAYER_A` */ - switch (combo_index) { - case SOME_COMBO: - if (layer_state_is(_LAYER_A)) { - return false; - } - } - - return true; -} -``` - -### Combo timer - -Normally, the timer is started on the first key press and then reset on every subsequent key press within the `COMBO_TERM`. -Inputting combos is relaxed like this, but also slightly more prone to accidental misfires. - -The next two options alter the behaviour of the timer. - -#### `#define COMBO_STRICT_TIMER` - -With `COMBO_STRICT_TIMER`, the timer is started only on the first key press. -Inputting combos is now less relaxed; you need to make sure the full chord is pressed within the `COMBO_TERM`. -Misfires are less common but if you type multiple combos fast, there is a -chance that the latter ones might not activate properly. - -#### `#define COMBO_NO_TIMER` - -By defining `COMBO_NO_TIMER`, the timer is disabled completely and combos are activated on the first key release. -This also disables the "must hold" functionalities as they just wouldn't work at all. - -### Customizable key releases - -By defining `COMBO_PROCESS_KEY_RELEASE` and implementing the function `bool process_combo_key_release(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode)`, you can run your custom code on each key release after a combo was activated. For example you could change the RGB colors, activate haptics, or alter the modifiers. - -You can also release a combo early by returning `true` from the function. - -Here's an example where a combo resolves to two modifiers, and on key releases the modifiers are unregistered one by one, depending on which key was released. - -```c -enum combos { - AB_MODS -}; - -const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END}; - -combo_t key_combos[] = { - [AB_MODS] = COMBO(ab_combo, LCTL(KC_LSFT)), -}; - -bool process_combo_key_release(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode) { - switch (combo_index) { - case AB_MODS: - switch(keycode) { - case KC_A: - unregister_mods(MOD_MASK_CTRL); - break; - case KC_B: - unregister_mods(MOD_MASK_SHIFT); - break; - } - return false; // do not release combo - } - return false; -} -``` -### Layer independent combos - -If you, for example, use multiple base layers for different key layouts, one for QWERTY, and another one for Colemak, you might want your combos to work from the same key positions on all layers. Defining the same combos again for another layout is redundant and takes more memory. The solution is to just check the keycodes from one layer. - -With `#define COMBO_ONLY_FROM_LAYER 0` in config.h, the combos' keys are always checked from layer `0`, even if other layers are active. - -#### Combo reference layers by layer. - -If not using `COMBO_ONLY_FROM_LAYER` it is possible to specify a -combo reference layer for any layer using the `combo_ref_from_layer` hook. -The combo macros automatically create this function from the `COMBO_REF_LAYER()` -entries given. - -This function returns the assigned reference layer for the current layer. -if there is no match, it returns the default reference layer if set, -or the current layer otherwise. A default layer can be set with -`DEFAULT_REF_LAYER(_MY_COMBO_REF_LAYER)` - -If not set, the default reference layer selection from the automatically generated -`combo-ref-from-layer()` will be the current layer. - -The following `combo_ref_from_layer` function -will give a reference layer of _QWERTY for the _DVORAK layer and -will give the _NAV layer as a reference to it's self. All other layers -will have the default for their combo reference layer. If the default -is not set, all other layers will reference themselves. - -```c -#define COMBO_REF_DEFAULT _MY_COMBO_LAYER - -uint8_t combo_ref_from_layer(uint8_t layer){ - switch (get_highest_layer(layer_state)){ - case _DVORAK: return _QWERTY; - case _NAV: return _NAV; - default: return _MY_COMBO_LAYER; - } - return layer; // important if default is not in case. -} -``` - -The equivalent definition using the combo macros is this: - -```c -COMBO_REF_LAYER(_DVORAK, _QWERTY) -COMBO_REF_LAYER(_NAV, _NAV) -DEFAULT_REF_LAYER(_MY_COMBO_LAYER). -``` - - -## User callbacks - -In addition to the keycodes, there are a few functions that you can use to set the status, or check it: - -|Function |Description | -|-----------|--------------------------------------------------------------------| -| `combo_enable()` | Enables the combo feature | -| `combo_disable()` | Disables the combo feature, and clears the combo buffer | -| `combo_toggle()` | Toggles the state of the combo feature | -| `is_combo_enabled()` | Returns the status of the combo feature state (true or false) | - - -## Dictionary Management - -Having 3 places to update when adding new combos or altering old ones does become cumbersome when you have a lot of combos. We can alleviate this with some magic! ... If you consider C macros magic. -First, you need to add `VPATH += keyboards/gboards` to your `rules.mk`. Next, include the file `g/keymap_combo.h` in your `keymap.c`. - -::: warning -This functionality uses the same `process_combo_event` function as `COMBO_ACTION` macros do, so you cannot use the function yourself in your keymap. Instead, you have to define the `case`s of the `switch` statement by themselves within `inject.h`, which `g/keymap_combo.h` will then include into the function. -::: - -Then, write your combos in `combos.def` file in the following manner: - -```c -// Alternate reference layers by layer -// Layer Reference layer -COMBO_REF_LAYER(_DVORAK, _QWERTY) // reference the qwerty layer for dvorak. -COMBO_REF_LAYER(_NAV, _NAV) // explicit reference to self instead of the default. - -// name result chord keys -COMB(AB_ESC, KC_ESC, KC_A, KC_B) -COMB(JK_TAB, KC_TAB, KC_J, KC_K) -COMB(JKL_SPC, KC_SPC, KC_J, KC_K, KC_L) -COMB(BSSL_CLR, KC_NO, KC_BSPC, KC_LSFT) // using KC_NO as the resulting keycode is the same as COMBO_ACTION before. -COMB(QW_UNDO, C(KC_Z), KC_Q, KC_W) -SUBS(TH_THE, "the", KC_T, KC_H) // SUBS uses SEND_STRING to output the given string. -... -``` - -For small to huge ready made dictionaries of combos, you can check out http://combos.gboards.ca/. diff --git a/docs/feature_command.md b/docs/feature_command.md deleted file mode 100644 index 4aba9cfb63..0000000000 --- a/docs/feature_command.md +++ /dev/null @@ -1,51 +0,0 @@ -# Command - -Command, formerly known as Magic, is a way to change your keyboard's behavior without having to flash or unplug it to use [Bootmagic Lite](feature_bootmagic). There is a lot of overlap between this functionality and the [Magic Keycodes](keycodes_magic). Wherever possible we encourage you to use that feature instead of Command. - -On some keyboards Command is disabled by default. If this is the case, it must be explicitly enabled in your `rules.mk`: - -```make -COMMAND_ENABLE = yes -``` - -## Usage - -To use Command, hold down the key combination defined by the `IS_COMMAND()` macro. By default this is Left Shift+Right Shift. Then, press the key corresponding to the command you want. For example, to output the current QMK version to the QMK Toolbox console, press Left Shift+Right Shift+`V`. - -## Configuration - -If you would like to change the key assignments for Command, `#define` these in your `config.h` at either the keyboard or keymap level. All keycode assignments here must omit the `KC_` prefix. - -|Define |Default |Description | -|------------------------------------|--------------------------------|------------------------------------------------| -|`IS_COMMAND()` |`(get_mods() == MOD_MASK_SHIFT)`|The key combination to activate Command | -|`MAGIC_KEY_SWITCH_LAYER_WITH_FKEYS` |`true` |Set default layer with the Function row | -|`MAGIC_KEY_SWITCH_LAYER_WITH_NKEYS` |`true` |Set default layer with the number keys | -|`MAGIC_KEY_SWITCH_LAYER_WITH_CUSTOM`|`false` |Set default layer with `MAGIC_KEY_LAYER0..9` | -|`MAGIC_KEY_DEBUG` |`D` |Toggle debugging over serial | -|`MAGIC_KEY_DEBUG_MATRIX` |`X` |Toggle key matrix debugging | -|`MAGIC_KEY_DEBUG_KBD` |`K` |Toggle keyboard debugging | -|`MAGIC_KEY_DEBUG_MOUSE` |`M` |Toggle mouse debugging | -|`MAGIC_KEY_CONSOLE` |`C` |Enable the Command console | -|`MAGIC_KEY_VERSION` |`V` |Print the running QMK version to the console | -|`MAGIC_KEY_STATUS` |`S` |Print the current keyboard status to the console| -|`MAGIC_KEY_HELP` |`H` |Print Command help to the console | -|`MAGIC_KEY_HELP_ALT` |`SLASH` |Print Command help to the console (alternate) | -|`MAGIC_KEY_LAYER0` |`0` |Make layer 0 the default layer | -|`MAGIC_KEY_LAYER0_ALT` |`GRAVE` |Make layer 0 the default layer (alternate) | -|`MAGIC_KEY_LAYER1` |`1` |Make layer 1 the default layer | -|`MAGIC_KEY_LAYER2` |`2` |Make layer 2 the default layer | -|`MAGIC_KEY_LAYER3` |`3` |Make layer 3 the default layer | -|`MAGIC_KEY_LAYER4` |`4` |Make layer 4 the default layer | -|`MAGIC_KEY_LAYER5` |`5` |Make layer 5 the default layer | -|`MAGIC_KEY_LAYER6` |`6` |Make layer 6 the default layer | -|`MAGIC_KEY_LAYER7` |`7` |Make layer 7 the default layer | -|`MAGIC_KEY_LAYER8` |`8` |Make layer 8 the default layer | -|`MAGIC_KEY_LAYER9` |`9` |Make layer 9 the default layer | -|`MAGIC_KEY_BOOTLOADER` |`B` |Jump to bootloader | -|`MAGIC_KEY_BOOTLOADER_ALT` |`ESC` |Jump to bootloader (alternate) | -|`MAGIC_KEY_LOCK` |`CAPS` |Lock the keyboard so nothing can be typed | -|`MAGIC_KEY_EEPROM` |`E` |Print stored EEPROM config to the console | -|`MAGIC_KEY_EEPROM_CLEAR` |`BSPACE` |Clear the EEPROM | -|`MAGIC_KEY_NKRO` |`N` |Toggle N-Key Rollover (NKRO) | -|`MAGIC_KEY_SLEEP_LED` |`Z` |Toggle LED when computer is sleeping | diff --git a/docs/feature_converters.md b/docs/feature_converters.md index 9b2d027a66..229d1007ab 100644 --- a/docs/feature_converters.md +++ b/docs/feature_converters.md @@ -39,7 +39,7 @@ qmk flash -c -kb keebio/bdn9/rev1 -km default -e CONVERT_TO=proton_c You can also add the same `CONVERT_TO=` to your keymap's `rules.mk`, which will accomplish the same thing. ::: tip -If you get errors about `PORTB/DDRB`, etc not being defined, you'll need to convert the keyboard's code to use the [GPIO Controls](gpio_control) that will work for both ARM and AVR. This shouldn't affect the AVR builds at all. +If you get errors about `PORTB/DDRB`, etc not being defined, you'll need to convert the keyboard's code to use the [GPIO Controls](drivers/gpio) that will work for both ARM and AVR. This shouldn't affect the AVR builds at all. ::: ### Conditional Configuration @@ -118,11 +118,11 @@ The following defaults are based on what has been implemented for STM32 boards. | Feature | Notes | |----------------------------------------------|------------------------------------------------------------------------------------------------------------------| -| [Audio](feature_audio) | Enabled | -| [RGB Lighting](feature_rgblight) | Disabled | -| [Backlight](feature_backlight) | Forces [task driven PWM](feature_backlight#software-pwm-driver) until ARM can provide automatic configuration | +| [Audio](features/audio) | Enabled | +| [RGB Lighting](features/rgblight) | Disabled | +| [Backlight](features/backlight) | Forces [task driven PWM](features/backlight#software-pwm-driver) until ARM can provide automatic configuration | | USB Host (e.g. USB-USB converter) | Not supported (USB host code is AVR specific and is not currently supported on ARM) | -| [Split keyboards](feature_split_keyboard) | Partial - heavily dependent on enabled features | +| [Split keyboards](features/split_keyboard) | Partial - heavily dependent on enabled features | ### Adafruit KB2040 {#kb2040} @@ -130,10 +130,10 @@ The following defaults are based on what has been implemented for [RP2040](platf | Feature | Notes | |----------------------------------------------|------------------------------------------------------------------------------------------------------------------| -| [RGB Lighting](feature_rgblight) | Enabled via `PIO` vendor driver | -| [Backlight](feature_backlight) | Forces [task driven PWM](feature_backlight#software-pwm-driver) until ARM can provide automatic configuration | +| [RGB Lighting](features/rgblight) | Enabled via `PIO` vendor driver | +| [Backlight](features/backlight) | Forces [task driven PWM](features/backlight#software-pwm-driver) until ARM can provide automatic configuration | | USB Host (e.g. USB-USB converter) | Not supported (USB host code is AVR specific and is not currently supported on ARM) | -| [Split keyboards](feature_split_keyboard) | Partial via `PIO` vendor driver - heavily dependent on enabled features | +| [Split keyboards](features/split_keyboard) | Partial via `PIO` vendor driver - heavily dependent on enabled features | ### SparkFun Pro Micro - RP2040, Blok, Bit-C PRO and Michi {#promicro_rp2040 } diff --git a/docs/feature_digitizer.md b/docs/feature_digitizer.md deleted file mode 100644 index 475d5b4d51..0000000000 --- a/docs/feature_digitizer.md +++ /dev/null @@ -1,119 +0,0 @@ -# Digitizer {#digitizer} - -Digitizers allow the mouse cursor to be placed at absolute coordinates, unlike the [Pointing Device](feature_pointing_device) feature which applies relative displacements. - -This feature implements a stylus device with a tip switch and barrel switch (generally equivalent to the primary and secondary mouse buttons respectively). Tip pressure is not currently implemented. - -## Usage {#usage} - -Add the following to your `rules.mk`: - -```make -DIGITIZER_ENABLE = yes -``` - -## Positioning {#positioning} - -The X and Y coordinates are normalized, meaning their value must be set between 0 and 1. For the X component, the value `0` is the leftmost position, whereas the value `1` is the rightmost position. Similarly for the Y component, `0` is at the top and `1` at the bottom. - -::: tip -Since there is no display attached, the OS will likely map these coordinates to the virtual desktop. This may be important to know if you have multiple monitors. -::: - -## Examples {#examples} - -This example simply places the cursor in the middle of the screen: - -```c -digitizer_in_range_on(); -digitizer_set_position(0.5, 0.5); -``` - -The "in range" indicator is required to be on for the change in coordinates to be taken. It can then be turned off again to signal the end of the digitizer interaction, but it is not strictly required. - -You can also modify the digitizer state directly, if you need to change multiple fields in a single report: - -```c -digitizer_state.in_range = true; -digitizer_state.dirty = true; -digitizer_flush(); -``` - -`digitizer_state` is a struct of type `digitizer_t`. - - -## API {#api} - -### `struct digitizer_t` {#api-digitizer-t} - -Contains the state of the digitizer. - -#### Members {#api-digitizer-t-members} - - - `bool in_range` - Indicates to the host that the contact is within range (ie. close to or in contact with the digitizer surface). - - `bool tip` - The state of the tip switch. - - `bool barrel` - The state of the barrel switch. - - `float x` - The X coordinate of the digitizer contact. - - `float y` - The Y coordinate of the digitizer contact. - - `bool dirty` - Whether the current state needs to be sent to the host. - ---- - -### `void digitizer_flush(void)` {#api-digitizer-flush} - -Send the digitizer report to the host if it is marked as dirty. - ---- - -### `void digitizer_in_range_on(void)` {#api-digitizer-in-range-on} - -Assert the "in range" indicator, and flush the report. - ---- - -### `void digitizer_in_range_off(void)` {#api-digitizer-in-range-off} - -Deassert the "in range" indicator, and flush the report. - ---- - -### `void digitizer_tip_switch_on(void)` {#api-digitizer-tip-switch-on} - -Assert the tip switch, and flush the report. - ---- - -### `void digitizer_tip_switch_off(void)` {#api-digitizer-tip-switch-off} - -Deassert the tip switch, and flush the report. - ---- - -### `void digitizer_barrel_switch_on(void)` {#api-digitizer-barrel-switch-on} - -Assert the barrel switch, and flush the report. - ---- - -### `void digitizer_barrel_switch_off(void)` {#api-digitizer-barrel-switch-off} - -Deassert the barrel switch, and flush the report. - ---- - -### `void digitizer_set_position(float x, float y)` {#api-digitizer-set-position} - -Set the absolute X and Y position of the digitizer contact, and flush the report. - -#### Arguments {#api-digitizer-set-position-arguments} - - - `float x` - The X value of the contact position, from 0 to 1. - - `float y` - The Y value of the contact position, from 0 to 1. diff --git a/docs/feature_dip_switch.md b/docs/feature_dip_switch.md deleted file mode 100644 index 738331bef0..0000000000 --- a/docs/feature_dip_switch.md +++ /dev/null @@ -1,132 +0,0 @@ -# DIP Switches - -DIP switches are supported by adding this to your `rules.mk`: - - DIP_SWITCH_ENABLE = yes - -and this to your `config.h`: - -```c -// Connects each switch in the dip switch to the GPIO pin of the MCU -#define DIP_SWITCH_PINS { B14, A15, A10, B9 } -// For split keyboards, you can separately define the right side pins -#define DIP_SWITCH_PINS_RIGHT { ... } -``` - -or - -```c -// Connect each switch in the DIP switch to an unused intersections in the key matrix. -#define DIP_SWITCH_MATRIX_GRID { {0,6}, {1,6}, {2,6} } // List of row and col pairs -``` - -## DIP Switch map {#dip-switch-map} - -DIP Switch mapping may be added to your `keymap.c`, which replicates the normal keyswitch functionality, but with dip switches. Add this to your keymap's `rules.mk`: - -```make -DIP_SWITCH_MAP_ENABLE = yes -``` - -Your `keymap.c` will then need a dip switch mapping defined (for two dip switches): - -```c -#if defined(DIP_SWITCH_MAP_ENABLE) -const uint16_t PROGMEM dip_switch_map[NUM_DIP_SWITCHES][NUM_DIP_STATES] = { - DIP_SWITCH_OFF_ON(DF(0), DF(1)), - DIP_SWITCH_OFF_ON(EC_NORM, EC_SWAP) -}; -#endif -``` - -::: tip -This should only be enabled at the keymap level. -::: - -## Callbacks - -The callback functions can be inserted into your `.c`: - -```c -bool dip_switch_update_kb(uint8_t index, bool active) { - if (!dip_switch_update_user(index, active)) { return false; } - return true; -} -``` - - -or `keymap.c`: - -```c -bool dip_switch_update_user(uint8_t index, bool active) { - switch (index) { - case 0: - if(active) { audio_on(); } else { audio_off(); } - break; - case 1: - if(active) { clicky_on(); } else { clicky_off(); } - break; - case 2: - if(active) { music_on(); } else { music_off(); } - break; - case 3: - if (active) { - #ifdef AUDIO_ENABLE - PLAY_SONG(plover_song); - #endif - layer_on(_PLOVER); - } else { - #ifdef AUDIO_ENABLE - PLAY_SONG(plover_gb_song); - #endif - layer_off(_PLOVER); - } - break; - } - return true; -} -``` - -Additionally, we support bit mask functions which allow for more complex handling. - - -```c -bool dip_switch_update_mask_kb(uint32_t state) { - if (!dip_switch_update_mask_user(state)) { return false; } - return true; -} -``` - - -or `keymap.c`: - -```c -bool dip_switch_update_mask_user(uint32_t state) { - if (state & (1UL<<0) && state & (1UL<<1)) { - layer_on(_ADJUST); // C on esc - } else { - layer_off(_ADJUST); - } - if (state & (1UL<<0)) { - layer_on(_TEST_A); // A on ESC - } else { - layer_off(_TEST_A); - } - if (state & (1UL<<1)) { - layer_on(_TEST_B); // B on esc - } else { - layer_off(_TEST_B); - } - return true; -} -``` - -## Hardware - -### Connects each switch in the dip switch to the GPIO pin of the MCU - -One side of the DIP switch should be wired directly to the pin on the MCU, and the other side to ground. It should not matter which side is connected to which, as it should be functionally the same. - -### Connect each switch in the DIP switch to an unused intersections in the key matrix. - -As with the keyswitch, a diode and DIP switch connect the ROW line to the COL line. diff --git a/docs/feature_dynamic_macros.md b/docs/feature_dynamic_macros.md deleted file mode 100644 index a642ced43e..0000000000 --- a/docs/feature_dynamic_macros.md +++ /dev/null @@ -1,69 +0,0 @@ -# Dynamic Macros: Record and Replay Macros in Runtime - -QMK supports temporary macros created on the fly. We call these Dynamic Macros. They are defined by the user from the keyboard and are lost when the keyboard is unplugged or otherwise rebooted. - -You can store one or two macros and they may have a combined total of 128 keypresses. You can increase this size at the cost of RAM. - -To enable them, first include `DYNAMIC_MACRO_ENABLE = yes` in your `rules.mk`. Then, add the following keys to your keymap: - -|Key |Alias |Description | -|---------------------------------|---------|--------------------------------------------------| -|`QK_DYNAMIC_MACRO_RECORD_START_1`|`DM_REC1`|Start recording Macro 1 | -|`QK_DYNAMIC_MACRO_RECORD_START_2`|`DM_REC2`|Start recording Macro 2 | -|`QK_DYNAMIC_MACRO_PLAY_1` |`DM_PLY1`|Replay Macro 1 | -|`QK_DYNAMIC_MACRO_PLAY_2` |`DM_PLY2`|Replay Macro 2 | -|`QK_DYNAMIC_MACRO_RECORD_STOP` |`DM_RSTP`|Finish the macro that is currently being recorded.| - -That should be everything necessary. - -To start recording the macro, press either `DM_REC1` or `DM_REC2`. - -To finish the recording, press the `DM_RSTP` layer button. You can also press `DM_REC1` or `DM_REC2` again to stop the recording. - -To replay the macro, press either `DM_PLY1` or `DM_PLY2`. - -It is possible to replay a macro as part of a macro. It's ok to replay macro 2 while recording macro 1 and vice versa but never create recursive macros i.e. macro 1 that replays macro 1. If you do so and the keyboard will get unresponsive, unplug the keyboard and plug it again. You can disable this completely by defining `DYNAMIC_MACRO_NO_NESTING` in your `config.h` file. - -::: tip -For the details about the internals of the dynamic macros, please read the comments in the `process_dynamic_macro.h` and `process_dynamic_macro.c` files. -::: - -## Customization - -There are a number of options added that should allow some additional degree of customization - -|Define |Default |Description | -|----------------------------|----------------|-----------------------------------------------------------------------------------------------------------------| -|`DYNAMIC_MACRO_SIZE` |128 |Sets the amount of memory that Dynamic Macros can use. This is a limited resource, dependent on the controller. | -|`DYNAMIC_MACRO_USER_CALL` |*Not defined* |Defining this falls back to using the user `keymap.c` file to trigger the macro behavior. | -|`DYNAMIC_MACRO_NO_NESTING` |*Not Defined* |Defining this disables the ability to call a macro from another macro (nested macros). | -|`DYNAMIC_MACRO_DELAY` |*Not Defined* |Sets the waiting time (ms unit) when sending each key. | - - -If the LEDs start blinking during the recording with each keypress, it means there is no more space for the macro in the macro buffer. To fit the macro in, either make the other macro shorter (they share the same buffer) or increase the buffer size by adding the `DYNAMIC_MACRO_SIZE` define in your `config.h` (default value: 128; please read the comments for it in the header). - - -### DYNAMIC_MACRO_USER_CALL - -For users of the earlier versions of dynamic macros: It is still possible to finish the macro recording using just the layer modifier used to access the dynamic macro keys, without a dedicated `DM_RSTP` key. If you want this behavior back, add `#define DYNAMIC_MACRO_USER_CALL` to your `config.h` and insert the following snippet at the beginning of your `process_record_user()` function: - -```c - uint16_t macro_kc = (keycode == MO(_DYN) ? DM_RSTP : keycode); - - if (!process_record_dynamic_macro(macro_kc, record)) { - return false; - } -``` - -### User Hooks - -There are a number of hooks that you can use to add custom functionality and feedback options to Dynamic Macro feature. This allows for some additional degree of customization. - -Note, that direction indicates which macro it is, with `1` being Macro 1, `-1` being Macro 2, and 0 being no macro. - -* `dynamic_macro_record_start_user(int8_t direction)` - Triggered when you start recording a macro. -* `dynamic_macro_play_user(int8_t direction)` - Triggered when you play back a macro. -* `dynamic_macro_record_key_user(int8_t direction, keyrecord_t *record)` - Triggered on each keypress while recording a macro. -* `dynamic_macro_record_end_user(int8_t direction)` - Triggered when the macro recording is stopped. - -Additionally, you can call `dynamic_macro_led_blink()` to flash the backlights if that feature is enabled. diff --git a/docs/feature_eeprom.md b/docs/feature_eeprom.md index 63026d3c10..2912407ac7 100644 --- a/docs/feature_eeprom.md +++ b/docs/feature_eeprom.md @@ -109,7 +109,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) { } } ``` -And lastly, you want to add the `eeconfig_init_user` function, so that when the EEPROM is reset, you can specify default values, and even custom actions. To force an EEPROM reset, use the `EE_CLR` keycode or [Bootmagic Lite](feature_bootmagic) functionallity. For example, if you want to set rgb layer indication by default, and save the default valued. +And lastly, you want to add the `eeconfig_init_user` function, so that when the EEPROM is reset, you can specify default values, and even custom actions. To force an EEPROM reset, use the `EE_CLR` keycode or [Bootmagic Lite](features/bootmagic) functionallity. For example, if you want to set rgb layer indication by default, and save the default valued. ```c void eeconfig_init_user(void) { // EEPROM is getting reset! diff --git a/docs/feature_encoders.md b/docs/feature_encoders.md deleted file mode 100644 index 3d1cac79af..0000000000 --- a/docs/feature_encoders.md +++ /dev/null @@ -1,186 +0,0 @@ -# Encoders - -Basic (EC11 compatible) encoders are supported by adding this to your `rules.mk`: - -```make -ENCODER_ENABLE = yes -``` - -and this to your `config.h`: - -```c -#define ENCODERS_PAD_A { B12 } -#define ENCODERS_PAD_B { B13 } -``` - -Each PAD_A/B variable defines an array so multiple encoders can be defined, e.g.: - -```c -#define ENCODERS_PAD_A { encoder1a, encoder2a } -#define ENCODERS_PAD_B { encoder1b, encoder2b } -``` - -If your encoder's clockwise directions are incorrect, you can swap the A & B pad definitions. They can also be flipped with a define: - -```c -#define ENCODER_DIRECTION_FLIP -``` - -Additionally, the resolution, which defines how many pulses the encoder registers between each detent, can be defined with: - -```c -#define ENCODER_RESOLUTION 4 -``` - -It can also be defined per-encoder, by instead defining: - -```c -#define ENCODER_RESOLUTIONS { 4, 2 } -``` - -For 4× encoders you also can assign default position if encoder skips pulses when it changes direction. For example, if your encoder send high level on both pins by default, define this: - -```c -#define ENCODER_DEFAULT_POS 0x3 -``` - -## Split Keyboards - -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 } -``` - -If the `_RIGHT` definitions aren't specified in your `config.h`, then the non-`_RIGHT` versions will be applied to both sides of the split. - -Additionally, if one side does not have an encoder, you can specify `{}` for the pins/resolution -- for example, a split keyboard with only a right-side encoder: - -```c -#define ENCODERS_PAD_A { } -#define ENCODERS_PAD_B { } -#define ENCODER_RESOLUTIONS { } -#define ENCODERS_PAD_A_RIGHT { B12 } -#define ENCODERS_PAD_B_RIGHT { B13 } -#define ENCODER_RESOLUTIONS_RIGHT { 4 } -``` - -::: warning -Keep in mind that whenver you change the encoder resolution, you will need to reflash the half that has the encoder affected by the change. -::: - -## Encoder map {#encoder-map} - -Encoder mapping may be added to your `keymap.c`, which replicates the normal keyswitch layer handling functionality, but with encoders. Add this to your keymap's `rules.mk`: - -```make -ENCODER_MAP_ENABLE = yes -``` - -Your `keymap.c` will then need an encoder mapping defined (for four layers and two encoders): - -```c -#if defined(ENCODER_MAP_ENABLE) -const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][NUM_DIRECTIONS] = { - [0] = { ENCODER_CCW_CW(KC_MS_WH_UP, KC_MS_WH_DOWN), ENCODER_CCW_CW(KC_VOLD, KC_VOLU) }, - [1] = { ENCODER_CCW_CW(RGB_HUD, RGB_HUI), ENCODER_CCW_CW(RGB_SAD, RGB_SAI) }, - [2] = { ENCODER_CCW_CW(RGB_VAD, RGB_VAI), ENCODER_CCW_CW(RGB_SPD, RGB_SPI) }, - [3] = { ENCODER_CCW_CW(RGB_RMOD, RGB_MOD), ENCODER_CCW_CW(KC_RIGHT, KC_LEFT) }, -}; -#endif -``` - -::: tip -This should only be enabled at the keymap level. -::: - -Using encoder mapping pumps events through the normal QMK keycode processing pipeline, resulting in a _keydown/keyup_ combination pushed through `process_record_xxxxx()`. To configure the amount of time between the encoder "keyup" and "keydown", you can add the following to your `config.h`: - -```c -#define ENCODER_MAP_KEY_DELAY 10 -``` - -::: tip -By default, the encoder map delay matches the value of `TAP_CODE_DELAY`. -::: - -## Callbacks - -::: tip -[**Default Behaviour**](https://github.com/qmk/qmk_firmware/blob/master/quantum/encoder.c#L79-): all encoders installed will function as volume up (`KC_VOLU`) on clockwise rotation and volume down (`KC_VOLD`) on counter-clockwise rotation. If you do not wish to override this, no further configuration is necessary. -::: - -If you would like the alter the default behaviour, and are not using `ENCODER_MAP_ENABLE = yes`, the callback functions can be inserted into your `.c`: - -```c -bool encoder_update_kb(uint8_t index, bool clockwise) { - if (!encoder_update_user(index, clockwise)) { - return false; /* Don't process further events if user function exists and returns false */ - } - if (index == 0) { /* First encoder */ - if (clockwise) { - tap_code(KC_PGDN); - } else { - tap_code(KC_PGUP); - } - } else if (index == 1) { /* Second encoder */ - if (clockwise) { - rgb_matrix_increase_hue(); - } else { - rgb_matrix_decrease_hue(); - } - } - return true; -} -``` - -or `keymap.c`: - -```c -bool encoder_update_user(uint8_t index, bool clockwise) { - if (index == 0) { /* First encoder */ - if (clockwise) { - tap_code(KC_PGDN); - } else { - tap_code(KC_PGUP); - } - } else if (index == 1) { /* Second encoder */ - if (clockwise) { - rgb_matrix_increase_hue(); - } else { - rgb_matrix_decrease_hue(); - } - } - return false; -} -``` - -::: warning -If you return `true` in the keymap level `_user` function, it will allow the keyboard/core level encoder code to run on top of your own. Returning `false` will override the keyboard level function, if setup correctly. This is generally the safest option to avoid confusion. -::: - -## Hardware - -The A an B lines of the encoders should be wired directly to the MCU, and the C/common lines should be wired to ground. - -## Multiple Encoders - -Multiple encoders may share pins so long as each encoder has a distinct pair of pins when the following conditions are met: -- using detent encoders -- pads must be high at the detent stability point which is called 'default position' in QMK -- no more than two encoders sharing a pin can be turned at the same time - -For example you can support two encoders using only 3 pins like this -``` -#define ENCODERS_PAD_A { B1, B1 } -#define ENCODERS_PAD_B { B2, B3 } -``` - -You could even support three encoders using only three pins (one per encoder) however in this configuration, rotating two encoders which share pins simultaneously will often generate incorrect output. For example: -``` -#define ENCODERS_PAD_A { B1, B1, B2 } -#define ENCODERS_PAD_B { B2, B3, B3 } -``` -Here rotating Encoder 0 `B1 B2` and Encoder 1 `B1 B3` could be interpreted as rotating Encoder 2 `B2 B3` or `B3 B2` depending on the timing. This may still be a useful configuration depending on your use case diff --git a/docs/feature_grave_esc.md b/docs/feature_grave_esc.md deleted file mode 100644 index 09d098ee4e..0000000000 --- a/docs/feature_grave_esc.md +++ /dev/null @@ -1,32 +0,0 @@ -# Grave Escape - -If you're using a 60% keyboard, or any other layout with no F-row, you will have noticed that there is no dedicated Escape key. Grave Escape is a feature that allows you to share the grave key (` and `~`) with Escape. - -## Usage - -Replace the `KC_GRV` key in your keymap (usually to the left of the `1` key) with `QK_GESC`. Most of the time this key will output `KC_ESC` when pressed. However, when Shift or GUI are held down it will output `KC_GRV` instead. - -## What Your OS Sees - -If Mary presses `QK_GESC` on her keyboard, the OS will see an KC_ESC character. Now if Mary holds Shift down and presses `QK_GESC` it will output `~`, or a shifted backtick. Now if she holds GUI/CMD/WIN, it will output a simple ` character. - -## Keycodes - -|Key |Aliases |Description | -|-----------------|---------|------------------------------------------------------------------| -|`QK_GRAVE_ESCAPE`|`QK_GESC`|Escape when pressed, ` when Shift or GUI are held| - -### Caveats - -On macOS, Command+` is by default mapped to "Move focus to next window" so it will not output a backtick. Additionally, Terminal always recognises this shortcut to cycle between windows, even if the shortcut is changed in the Keyboard preferences. - -## Configuration - -There are several possible key combinations this will break, among them Control+Shift+Escape on Windows and Command+Option+Escape on macOS. To work around this, you can `#define` these options in your `config.h`: - -|Define |Description | -|--------------------------|-----------------------------------------| -|`GRAVE_ESC_ALT_OVERRIDE` |Always send Escape if Alt is pressed | -|`GRAVE_ESC_CTRL_OVERRIDE` |Always send Escape if Control is pressed | -|`GRAVE_ESC_GUI_OVERRIDE` |Always send Escape if GUI is pressed | -|`GRAVE_ESC_SHIFT_OVERRIDE`|Always send Escape if Shift is pressed | diff --git a/docs/feature_haptic_feedback.md b/docs/feature_haptic_feedback.md deleted file mode 100644 index 16370327cb..0000000000 --- a/docs/feature_haptic_feedback.md +++ /dev/null @@ -1,214 +0,0 @@ -# Haptic Feedback - -## Haptic feedback rules.mk options - -The following options are currently available for haptic feedback in `rules.mk`: - -```make -HAPTIC_ENABLE = yes - -HAPTIC_DRIVER = drv2605l -# or -HAPTIC_DRIVER = solenoid -``` - -The following `config.h` settings are available for all types of haptic feedback: - -| Settings | Default | Description | -|--------------------------------------|---------------|---------------------------------------------------------------------------------------------------------------| -|`HAPTIC_ENABLE_PIN` | *Not defined* |Configures a pin to enable a boost converter for some haptic solution, often used with solenoid drivers. | -|`HAPTIC_ENABLE_PIN_ACTIVE_LOW` | *Not defined* |If defined then the haptic enable pin is active-low. | -|`HAPTIC_ENABLE_STATUS_LED` | *Not defined* |Configures a pin to reflect the current enabled/disabled status of haptic feedback. | -|`HAPTIC_ENABLE_STATUS_LED_ACTIVE_LOW` | *Not defined* |If defined then the haptic status led will be active-low. | -|`HAPTIC_OFF_IN_LOW_POWER` | `0` |If set to `1`, haptic feedback is disabled before the device is configured, and while the device is suspended. | - -## Known Supported Hardware - -| Name | Description | -|--------------------|-------------------------------------------------| -| [LV061228B-L65-A](https://www.digikey.com/product-detail/en/jinlong-machinery-electronics-inc/LV061228B-L65-A/1670-1050-ND/7732325) | z-axis 2v LRA | -| [Mini Motor Disc](https://www.adafruit.com/product/1201) | small 2-5v ERM | - -## Haptic Keycodes - -Not all keycodes below will work depending on which haptic mechanism you have chosen. - -| Key | Aliases | Description | -|-----------------------------|---------|-------------------------------------------------------| -|`QK_HAPTIC_ON` |`HF_ON` | Turn haptic feedback on | -|`QK_HAPTIC_OFF` |`HF_OFF` | Turn haptic feedback off | -|`QK_HAPTIC_TOGGLE` |`HF_TOGG`| Toggle haptic feedback on/off | -|`QK_HAPTIC_RESET` |`HF_RST` | Reset haptic feedback config to default | -|`QK_HAPTIC_FEEDBACK_TOGGLE` |`HF_FDBK`| Toggle feedback to occur on keypress, release or both | -|`QK_HAPTIC_BUZZ_TOGGLE` |`HF_BUZZ`| Toggle solenoid buzz on/off | -|`QK_HAPTIC_MODE_NEXT` |`HF_NEXT`| Go to next DRV2605L waveform | -|`QK_HAPTIC_MODE_PREVIOUS` |`HF_PREV`| Go to previous DRV2605L waveform | -|`QK_HAPTIC_CONTINUOUS_TOGGLE`|`HF_CONT`| Toggle continuous haptic mode on/off | -|`QK_HAPTIC_CONTINUOUS_UP` |`HF_CONU`| Increase DRV2605L continous haptic strength | -|`QK_HAPTIC_CONTINUOUS_DOWN` |`HF_COND`| Decrease DRV2605L continous haptic strength | -|`QK_HAPTIC_DWELL_UP` |`HF_DWLU`| Increase Solenoid dwell time | -|`QK_HAPTIC_DWELL_DOWN` |`HF_DWLD`| Decrease Solenoid dwell time | - -### Solenoids - -The solenoid code supports relay switches, and similar hardware, as well as solenoids. - -For a regular solenoid, you will need a build a circuit to drive the solenoid through a mosfet as most MCU will not be able to provide the current needed to drive the coil in the solenoid. - -[Wiring diagram provided by Adafruit](https://cdn-shop.adafruit.com/product-files/412/solenoid_driver.pdf) - -For relay switches, the hardware may already contain all of that ciruitry, and just require VCC, GND and a data pin. - -| Settings | Default | Description | -|----------------------------|----------------------|--------------------------------------------------------------| -|`SOLENOID_PIN` | *Not defined* |Configures the pin that the switch is connected to. | -|`SOLENOID_PIN_ACTIVE_LOW` | *Not defined* |If defined then the switch trigger pin is active low. | -|`SOLENOID_PINS` | *Not defined* |Configures an array of pins to be used for switch activation. | -|`SOLENOID_PINS_ACTIVE_LOW` | *Not defined* |Allows you to specify how each pin is pulled for activation. | -|`SOLENOID_RANDOM_FIRE` | *Not defined* |When there are multiple solenoids, will select a random one to fire.| -|`SOLENOID_DEFAULT_DWELL` | `12` ms |Configures the default dwell time for the switch. | -|`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 `HF_DWL*` keycodes are sent. | -|`SOLENOID_DEFAULT_BUZZ` | `0` (disabled) |On `HF_RST` buzz is set "on" if this is "1" | -|`SOLENOID_BUZZ_ACTUATED` | `SOLENOID_MIN_DWELL` |Actuated-time when the switch is in buzz mode. | -|`SOLENOID_BUZZ_NONACTUATED` | `SOLENOID_MIN_DWELL` |Non-Actuated-time when the switch 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. - -### DRV2605L - -DRV2605L is controlled over i2c protocol, and has to be connected to the SDA and SCL pins, these varies depending on the MCU in use. - -#### Feedback motor setup - -This driver supports 2 different feedback motors. Set the following in your `config.h` based on which motor you have selected. - -##### ERM - -Eccentric Rotating Mass vibration motors (ERM) is motor with a off-set weight attached so when drive signal is attached, the off-set weight spins and causes a sinusoidal wave that translate into vibrations. - -```c -#define DRV2605L_FB_ERM_LRA 0 -#define DRV2605L_FB_BRAKEFACTOR 3 /* For 1x:0, 2x:1, 3x:2, 4x:3, 6x:4, 8x:5, 16x:6, Disable Braking:7 */ -#define DRV2605L_FB_LOOPGAIN 1 /* For Low:0, Medium:1, High:2, Very High:3 */ - -/* Please refer to your datasheet for the optimal setting for your specific motor. */ -#define DRV2605L_RATED_VOLTAGE 3 -#define DRV2605L_V_PEAK 5 -``` -##### LRA - -Linear resonant actuators (LRA, also know as a linear vibrator) works different from a ERM. A LRA has a weight and magnet suspended by springs and a voice coil. When the drive signal is applied, the weight would be vibrate on a single axis (side to side or up and down). Since the weight is attached to a spring, there is a resonance effect at a specific frequency. This frequency is where the LRA will operate the most efficiently. Refer to the motor's datasheet for the recommanded range for this frequency. - -```c -#define DRV2605L_FB_ERM_LRA 1 -#define DRV2605L_FB_BRAKEFACTOR 3 /* For 1x:0, 2x:1, 3x:2, 4x:3, 6x:4, 8x:5, 16x:6, Disable Braking:7 */ -#define DRV2605L_FB_LOOPGAIN 1 /* For Low:0, Medium:1, High:2, Very High:3 */ - -/* Please refer to your datasheet for the optimal setting for your specific motor. */ -#define DRV2605L_RATED_VOLTAGE 2 -#define DRV2605L_V_PEAK 2.8 -#define DRV2605L_V_RMS 2.0 -#define DRV2605L_V_PEAK 2.1 -#define DRV2605L_F_LRA 205 /* resonance freq */ -``` - -#### DRV2605L waveform library - -DRV2605L comes with preloaded library of various waveform sequences that can be called and played. If writing a macro, these waveforms can be played using `DRV_pulse(*sequence name or number*)` - -List of waveform sequences from the datasheet: - -|seq# | Sequence name |seq# | Sequence name |seq# |Sequence name | -|-----|---------------------|-----|-----------------------------------|-----|--------------------------------------| -| 1 | strong_click | 43 | lg_dblclick_med_60 | 85 | transition_rampup_med_smooth2 | -| 2 | strong_click_60 | 44 | lg_dblsharp_tick | 86 | transition_rampup_short_smooth1 | -| 3 | strong_click_30 | 45 | lg_dblsharp_tick_80 | 87 | transition_rampup_short_smooth2 | -| 4 | sharp_click | 46 | lg_dblsharp_tick_60 | 88 | transition_rampup_long_sharp1 | -| 5 | sharp_click_60 | 47 | buzz | 89 | transition_rampup_long_sharp2 | -| 6 | sharp_click_30 | 48 | buzz_80 | 90 | transition_rampup_med_sharp1 | -| 7 | soft_bump | 49 | buzz_60 | 91 | transition_rampup_med_sharp2 | -| 8 | soft_bump_60 | 50 | buzz_40 | 92 | transition_rampup_short_sharp1 | -| 9 | soft_bump_30 | 51 | buzz_20 | 93 | transition_rampup_short_sharp2 | -| 10 | dbl_click | 52 | pulsing_strong | 94 | transition_rampdown_long_smooth1_50 | -| 11 | dbl_click_60 | 53 | pulsing_strong_80 | 95 | transition_rampdown_long_smooth2_50 | -| 12 | trp_click | 54 | pulsing_medium | 96 | transition_rampdown_med_smooth1_50 | -| 13 | soft_fuzz | 55 | pulsing_medium_80 | 97 | transition_rampdown_med_smooth2_50 | -| 14 | strong_buzz | 56 | pulsing_sharp | 98 | transition_rampdown_short_smooth1_50 | -| 15 | alert_750ms | 57 | pulsing_sharp_80 | 99 | transition_rampdown_short_smooth2_50 | -| 16 | alert_1000ms | 58 | transition_click | 100 | transition_rampdown_long_sharp1_50 | -| 17 | strong_click1 | 59 | transition_click_80 | 101 | transition_rampdown_long_sharp2_50 | -| 18 | strong_click2_80 | 60 | transition_click_60 | 102 | transition_rampdown_med_sharp1_50 | -| 19 | strong_click3_60 | 61 | transition_click_40 | 103 | transition_rampdown_med_sharp2_50 | -| 20 | strong_click4_30 | 62 | transition_click_20 | 104 | transition_rampdown_short_sharp1_50 | -| 21 | medium_click1 | 63 | transition_click_10 | 105 | transition_rampdown_short_sharp2_50 | -| 22 | medium_click2_80 | 64 | transition_hum | 106 | transition_rampup_long_smooth1_50 | -| 23 | medium_click3_60 | 65 | transition_hum_80 | 107 | transition_rampup_long_smooth2_50 | -| 24 | sharp_tick1 | 66 | transition_hum_60 | 108 | transition_rampup_med_smooth1_50 | -| 25 | sharp_tick2_80 | 67 | transition_hum_40 | 109 | transition_rampup_med_smooth2_50 | -| 26 | sharp_tick3_60 | 68 | transition_hum_20 | 110 | transition_rampup_short_smooth1_50 | -| 27 | sh_dblclick_str | 69 | transition_hum_10 | 111 | transition_rampup_short_smooth2_50 | -| 28 | sh_dblclick_str_80 | 70 | transition_rampdown_long_smooth1 | 112 | transition_rampup_long_sharp1_50 | -| 29 | sh_dblclick_str_60 | 71 | transition_rampdown_long_smooth2 | 113 | transition_rampup_long_sharp2_50 | -| 30 | sh_dblclick_str_30 | 72 | transition_rampdown_med_smooth1 | 114 | transition_rampup_med_sharp1_50 | -| 31 | sh_dblclick_med | 73 | transition_rampdown_med_smooth2 | 115 | transition_rampup_med_sharp2_50 | -| 32 | sh_dblclick_med_80 | 74 | transition_rampdown_short_smooth1 | 116 | transition_rampup_short_sharp1_50 | -| 33 | sh_dblclick_med_60 | 75 | transition_rampdown_short_smooth2 | 117 | transition_rampup_short_sharp2_50 | -| 34 | sh_dblsharp_tick | 76 | transition_rampdown_long_sharp1 | 118 | long_buzz_for_programmatic_stopping | -| 35 | sh_dblsharp_tick_80 | 77 | transition_rampdown_long_sharp2 | 119 | smooth_hum1_50 | -| 36 | sh_dblsharp_tick_60 | 78 | transition_rampdown_med_sharp1 | 120 | smooth_hum2_40 | -| 37 | lg_dblclick_str | 79 | transition_rampdown_med_sharp2 | 121 | smooth_hum3_30 | -| 38 | lg_dblclick_str_80 | 80 | transition_rampdown_short_sharp1 | 122 | smooth_hum4_20 | -| 39 | lg_dblclick_str_60 | 81 | transition_rampdown_short_sharp2 | 123 | smooth_hum5_10 | -| 40 | lg_dblclick_str_30 | 82 | transition_rampup_long_smooth1 | | | -| 41 | lg_dblclick_med | 83 | transition_rampup_long_smooth2 | | | -| 42 | lg_dblclick_med_80 | 84 | transition_rampup_med_smooth1 | | | -### Optional DRV2605L defines - -```c -#define DRV2605L_GREETING *sequence name or number* -``` -If haptic feedback is enabled, the keyboard will vibrate to a specific sequence during startup. That can be selected using the following define: - -```c -#define DRV2605L_DEFAULT_MODE *sequence name or number* -``` -This will set what sequence `HF_RST` will set as the active mode. If not defined, mode will be set to 1 when `HF_RST` is pressed. - -### DRV2605L Continuous Haptic Mode - -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, the following keys will not trigger feedback: - -* Usual modifier keys such as Control/Shift/Alt/Gui (For example `KC_LCTL`) -* `MO()` momentary keys. See also [Layers](feature_layers). -* `LM()` momentary keys with mod active. -* `LT()` layer tap keys, when held to activate a layer. However when tapped, and the key is quickly released, and sends a keycode, haptic feedback is still triggered. -* `TT()` layer tap toggle keys, when held to activate a layer. However when tapped `TAPPING_TOGGLE` times to permanently toggle the layer, on the last tap haptic feedback is still triggered. -* `MT()` mod tap keys, when held to keep a usual modifier key pressed. However when tapped, and the key is quickly released, and sends a keycode, haptic feedback is still triggered. See also [Mod-Tap](mod_tap). - -### 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. diff --git a/docs/feature_hd44780.md b/docs/feature_hd44780.md deleted file mode 100644 index 6b4209333b..0000000000 --- a/docs/feature_hd44780.md +++ /dev/null @@ -1,298 +0,0 @@ -# HD44780 LCD Driver {#hd44780-lcd-driver} - -## Supported Hardware {#supported-hardware} - -LCD modules using [HD44780U](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) IC or equivalent, communicating in 4-bit mode. - -|Module|Size |Notes | -|------|--------------|---------------------------------| -|1602A |16x2, 5x8 dots| | -|2004A |20x4, 5x8 dots|Untested, not currently supported| - -To run these modules at 3.3V, an additional MAX660 voltage converter IC must be soldered on, along with two 10µF capacitors. See [this page](https://www.codrey.com/electronic-circuits/hack-your-16x2-lcd/) for more details. - -## Usage {#usage} - -Add the following to your `rules.mk`: - -```make -HD44780_ENABLE = yes -``` - -## Basic Configuration {#basic-configuration} - -Add the following to your `config.h`: - -|Define |Default |Description | -|-----------------------|--------------|-----------------------------------------------------------------------------------------------------| -|`HD44780_DATA_PINS` |*Not defined* |(Required) An array of four GPIO pins connected to the display's D4-D7 pins, eg. `{ B1, B3, B2, B6 }`| -|`HD44780_RS_PIN` |*Not defined* |(Required) The GPIO connected to the display's RS pin | -|`HD44780_RW_PIN` |*Not defined* |(Required) The GPIO connected to the display's RW pin | -|`HD44780_E_PIN` |*Not defined* |(Required) The GPIO connected to the display's E pin | -|`HD44780_DISPLAY_COLS` |`16` |The number of visible characters on a single line of the display | -|`HD44780_DISPLAY_LINES`|`2` |The number of visible lines on the display | -|`HD44780_WRAP_LINES` |*Not defined* |If defined, input characters will wrap to the next line | - -## Examples {#examples} - -### Hello World {#example-hello-world} - -Add the following to your `keymap.c`: - -```c -void keyboard_post_init_user(void) { - hd44780_init(true, true); // Show blinking cursor - hd44780_puts_P(PSTR("Hello, world!\n")); -} -``` - -### Custom Character Definition {#example-custom-character} - -Up to eight custom characters can be defined. This data is stored in the Character Generator RAM (CGRAM), and is not persistent across power cycles. - -This example defines the QMK Psi as the first custom character. The first 16 positions in the character set are reserved for the eight custom characters duplicated. - -``` -Byte | 16 8 4 2 1 - 1 | x x x ■ □ ■ □ ■ - 2 | x x x ■ □ ■ □ ■ - 3 | x x x ■ □ ■ □ ■ - 4 | x x x □ ■ ■ ■ □ - 5 | x x x □ □ ■ □ □ - 6 | x x x □ □ ■ □ □ - 7 | x x x □ □ ■ □ □ - 8 | x x x □ □ □ □ □ -``` - -```c -const uint8_t PROGMEM psi[8] = { 0x15, 0x15, 0x15, 0x0E, 0x04, 0x04, 0x04, 0x00 }; - -void keyboard_post_init_user(void) { - hd44780_init(false, false); - hd44780_define_char_P(0, psi); - // Cursor is incremented while defining characters so must be reset - hd44780_home(); - // 0x08 to avoid null terminator - hd44780_puts_P(PSTR("\x08 QMK Firmware")); -} -``` - -## API {#api} - -### `void hd44780_init(bool cursor, bool blink)` {#api-hd44780-init} - -Initialize the display. - -This function should be called only once, before any of the other functions can be called. - -#### Arguments {#api-hd44780-init-arguments} - - - `bool cursor` - Whether to show the cursor. - - `bool blink` - Whether to blink the cursor, if shown. - ---- - -### `void hd44780_clear(void)` {#api-hd44780-clear} - -Clear the display. - -This function is called on init. - ---- - -### `void hd44780_home(void)` {#api-hd44780-home} - -Move the cursor to the home position. - -This function is called on init. - ---- - -### `void hd44780_on(bool cursor, bool blink)` {#api-hd44780-on} - -Turn the display on, and/or set the cursor properties. - -This function is called on init. - -#### Arguments {#api-hd44780-on-arguments} - - - `bool cursor` - Whether to show the cursor. - - `bool blink` - Whether to blink the cursor, if shown. - ---- - -### `void hd44780_off(void)` {#api-hd44780-off} - -Turn the display off. - ---- - -### `void hd44780_set_cursor(uint8_t col, uint8_t line)` {#api-hd44780-set-cursor} - -Move the cursor to the specified position on the display. - -#### Arguments {#api-hd44780-set-cursor-arguments} - - - `uint8_t col` - The column number to move to, from 0 to 15 on 16x2 displays. - - `bool line` - The line number to move to, either 0 or 1 on 16x2 displays. - ---- - -### `void hd44780_putc(char c)` {#api-hd44780-putc} - -Print a character to the display. The newline character `\n` will move the cursor to the start of the next line. - -The exact character shown may depend on the ROM code of your particular display - refer to the datasheet for the full character set. - -#### Arguments {#api-hd44780-putc-arguments} - - - `char c` - The character to print. - ---- - -### `void hd44780_puts(const char *s)` {#api-hd44780-puts} - -Print a string of characters to the display. - -#### Arguments {#api-hd44780-puts-arguments} - - - `const char *s` - The string to print. - ---- - -### `void hd44780_puts_P(const char *s)` {#api-hd44780-puts-p} - -Print a string of characters from PROGMEM to the display. - -On ARM devices, this function is simply an alias of `hd44780_puts()`. - -#### Arguments {#api-hd44780-puts-p-arguments} - - - `const char *s` - The PROGMEM string to print (ie. `PSTR("Hello")`). - ---- - -### `void hd44780_define_char(uint8_t index, uint8_t *data)` {#api-hd44780-define-char} - -Define a custom character. - -#### Arguments {#api-hd44780-define-char-arguments} - - - `uint8_t index` - The index of the custom character to define, from 0 to 7. - - `uint8_t *data` - An array of 8 bytes containing the 5-bit row data of the character, where the first byte is the topmost row, and the least significant bit of each byte is the rightmost column. - ---- - -### `void hd44780_define_char_P(uint8_t index, const uint8_t *data)` {#api-hd44780-define-char-p} - -Define a custom character from PROGMEM. - -On ARM devices, this function is simply an alias of `hd44780_define_char()`. - -#### Arguments {#api-hd44780-define-char-p-arguments} - - - `uint8_t index` - The index of the custom character to define, from 0 to 7. - - `const uint8_t *data` - A PROGMEM array of 8 bytes containing the 5-bit row data of the character, where the first byte is the topmost row, and the least significant bit of each byte is the rightmost column. - ---- - -### `bool hd44780_busy(void)` {#api-hd44780-busy} - -Indicates whether the display is currently processing, and cannot accept instructions. - -#### Return Value {#api-hd44780-busy-arguments} - -`true` if the display is busy. - ---- - -### `void hd44780_write(uint8_t data, bool isData)` {#api-hd44780-write} - -Write a byte to the display. - -#### Arguments {#api-hd44780-write-arguments} - - - `uint8_t data` - The byte to send to the display. - - `bool isData` - Whether the byte is an instruction or character data. - ---- - -### `uint8_t hd44780_read(bool isData)` {#api-hd44780-read} - -Read a byte from the display. - -#### Arguments {#api-hd44780-read-arguments} - - - `bool isData` - Whether to read the current cursor position, or the character at the cursor. - -#### Return Value {#api-hd44780-read-return} - -If `isData` is `true`, the returned byte will be the character at the current DDRAM address. Otherwise, it will be the current DDRAM address and the busy flag. - ---- - -### `void hd44780_command(uint8_t command)` {#api-hd44780-command} - -Send a command to the display. Refer to the datasheet and `hd44780.h` for the valid commands and defines. - -This function waits for the display to clear the busy flag before sending the command. - -#### Arguments {#api-hd44780-command-arguments} - - - `uint8_t command` - The command to send. - ---- - -### `void hd44780_data(uint8_t data)` {#api-hd44780-data} - -Send a byte of data to the display. - -This function waits for the display to clear the busy flag before sending the data. - -#### Arguments {#api-hd44780-data-arguments} - - - `uint8_t data` - The byte of data to send. - ---- - -### `void hd44780_set_cgram_address(uint8_t address)` {#api-hd44780-set-cgram-address} - -Set the CGRAM address. - -This function is used when defining custom characters. - -#### Arguments {#api-hd44780-set-cgram-address-arguments} - - - `uint8_t address` - The CGRAM address to move to, from `0x00` to `0x3F`. - ---- - -### `void hd44780_set_ddram_address(uint8_t address)` {#api-hd44780-set-ddram-address} - -Set the DDRAM address. - -This function is used when printing characters to the display, and setting the cursor. - -#### Arguments {#api-hd44780-set-ddram-address-arguments} - - - `uint8_t address` - The DDRAM address to move to, from `0x00` to `0x7F`. diff --git a/docs/feature_joystick.md b/docs/feature_joystick.md deleted file mode 100644 index 75bffa605a..0000000000 --- a/docs/feature_joystick.md +++ /dev/null @@ -1,222 +0,0 @@ -# Joystick {#joystick} - -This feature provides game controller input as a joystick device supporting up to 6 axes and 32 buttons. Axes can be read either from an [ADC-capable input pin](adc_driver), or can be virtual, so that its value is provided by your code. - -An analog device such as a [potentiometer](https://en.wikipedia.org/wiki/Potentiometer) found on an analog joystick's axes is based on a voltage divider, where adjusting the movable wiper controls the output voltage which can then be read by the microcontroller's ADC. - -## Usage {#usage} - -Add the following to your `rules.mk`: - -```make -JOYSTICK_ENABLE = yes -``` - -By default the joystick driver is `analog`, but you can change this with: - -```make -JOYSTICK_DRIVER = digital -``` - -## Configuration {#configuration} - -By default, two axes and eight buttons are defined, with a reported resolution of 8 bits (-127 to +127). This can be changed in your `config.h`: - -```c -// Min 0, max 32 -#define JOYSTICK_BUTTON_COUNT 16 -// Min 0, max 6: X, Y, Z, Rx, Ry, Rz -#define JOYSTICK_AXIS_COUNT 3 -// Min 8, max 16 -#define JOYSTICK_AXIS_RESOLUTION 10 -``` - -::: tip -You must define at least one button or axis. Also note that the maximum ADC resolution of the supported AVR MCUs is 10-bit, and 12-bit for most STM32 MCUs. -::: - -### Axes {#axes} - -When defining axes for your joystick, you must provide a definition array typically in your `keymap.c`. - -For instance, the below example configures two axes. The X axis is read from the `A4` pin. With the default axis resolution of 8 bits, the range of values between 900 and 575 are scaled to -127 through 0, and values 575 to 285 are scaled to 0 through 127. The Y axis is configured as a virtual axis, and its value is not read from any pin. Instead, the user must update the axis value programmatically. - -```c -joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = { - JOYSTICK_AXIS_IN(A4, 900, 575, 285), - JOYSTICK_AXIS_VIRTUAL -}; -``` - -Axes can be configured using one of the following macros: - - * `JOYSTICK_AXIS_IN(input_pin, low, rest, high)` - The ADC samples the provided pin. `low`, `high` and `rest` correspond to the minimum, maximum, and resting (or centered) analog values of the axis, respectively. - * `JOYSTICK_AXIS_VIRTUAL` - No ADC reading is performed. The value should be provided by user code. - -The `low` and `high` values can be swapped to effectively invert the axis. - -#### Virtual Axes {#virtual-axes} - -The following example adjusts two virtual axes (X and Y) based on keypad presses, with `KC_P0` as a precision modifier: - -```c -joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = { - JOYSTICK_AXIS_VIRTUAL, // x - JOYSTICK_AXIS_VIRTUAL // y -}; - -static bool precision = false; -static uint16_t precision_mod = 64; -static uint16_t axis_val = 127; - -bool process_record_user(uint16_t keycode, keyrecord_t *record) { - int16_t precision_val = axis_val; - if (precision) { - precision_val -= precision_mod; - } - - switch (keycode) { - case KC_P8: - joystick_set_axis(1, record->event.pressed ? -precision_val : 0); - return false; - case KC_P2: - joystick_set_axis(1, record->event.pressed ? precision_val : 0); - return false; - case KC_P4: - joystick_set_axis(0, record->event.pressed ? -precision_val : 0); - return false; - case KC_P6: - joystick_set_axis(0, record->event.pressed ? precision_val : 0); - return false; - case KC_P0: - precision = record->event.pressed; - return false; - } - return true; -} -``` - -## Keycodes {#keycodes} - -|Key |Aliases|Description| -|-----------------------|-------|-----------| -|`QK_JOYSTICK_BUTTON_0` |`JS_0` |Button 0 | -|`QK_JOYSTICK_BUTTON_1` |`JS_1` |Button 1 | -|`QK_JOYSTICK_BUTTON_2` |`JS_2` |Button 2 | -|`QK_JOYSTICK_BUTTON_3` |`JS_3` |Button 3 | -|`QK_JOYSTICK_BUTTON_4` |`JS_4` |Button 4 | -|`QK_JOYSTICK_BUTTON_5` |`JS_5` |Button 5 | -|`QK_JOYSTICK_BUTTON_6` |`JS_6` |Button 6 | -|`QK_JOYSTICK_BUTTON_7` |`JS_7` |Button 7 | -|`QK_JOYSTICK_BUTTON_8` |`JS_8` |Button 8 | -|`QK_JOYSTICK_BUTTON_9` |`JS_9` |Button 9 | -|`QK_JOYSTICK_BUTTON_10`|`JS_10`|Button 10 | -|`QK_JOYSTICK_BUTTON_11`|`JS_11`|Button 11 | -|`QK_JOYSTICK_BUTTON_12`|`JS_12`|Button 12 | -|`QK_JOYSTICK_BUTTON_13`|`JS_13`|Button 13 | -|`QK_JOYSTICK_BUTTON_14`|`JS_14`|Button 14 | -|`QK_JOYSTICK_BUTTON_15`|`JS_15`|Button 15 | -|`QK_JOYSTICK_BUTTON_16`|`JS_16`|Button 16 | -|`QK_JOYSTICK_BUTTON_17`|`JS_17`|Button 17 | -|`QK_JOYSTICK_BUTTON_18`|`JS_18`|Button 18 | -|`QK_JOYSTICK_BUTTON_19`|`JS_19`|Button 19 | -|`QK_JOYSTICK_BUTTON_20`|`JS_20`|Button 20 | -|`QK_JOYSTICK_BUTTON_21`|`JS_21`|Button 21 | -|`QK_JOYSTICK_BUTTON_22`|`JS_22`|Button 22 | -|`QK_JOYSTICK_BUTTON_23`|`JS_23`|Button 23 | -|`QK_JOYSTICK_BUTTON_24`|`JS_24`|Button 24 | -|`QK_JOYSTICK_BUTTON_25`|`JS_25`|Button 25 | -|`QK_JOYSTICK_BUTTON_26`|`JS_26`|Button 26 | -|`QK_JOYSTICK_BUTTON_27`|`JS_27`|Button 27 | -|`QK_JOYSTICK_BUTTON_28`|`JS_28`|Button 28 | -|`QK_JOYSTICK_BUTTON_29`|`JS_29`|Button 29 | -|`QK_JOYSTICK_BUTTON_30`|`JS_30`|Button 30 | -|`QK_JOYSTICK_BUTTON_31`|`JS_31`|Button 31 | - -## API {#api} - -### `struct joystick_t` {#api-joystick-t} - -Contains the state of the joystick. - -#### Members {#api-joystick-t-members} - - - `uint8_t buttons[]` - A bit-packed array containing the joystick button states. The size is calculated as `(JOYSTICK_BUTTON_COUNT - 1) / 8 + 1`. - - `int16_t axes[]` - An array of analog values for each defined axis. - - `bool dirty` - Whether the current state needs to be sent to the host. - ---- - -### `struct joystick_config_t` {#api-joystick-config-t} - -Describes a single axis. - -#### Members {#api-joystick-config-t-members} - - - `pin_t input_pin` - The pin to read the analog value from, or `JS_VIRTUAL_AXIS`. - - `uint16_t min_digit` - The minimum analog value. - - `uint16_t mid_digit` - The resting or midpoint analog value. - - `uint16_t max_digit` - The maximum analog value. - ---- - -### `void joystick_flush(void)` {#api-joystick-flush} - -Send the joystick report to the host, if it has been marked as dirty. - ---- - -### `void register_joystick_button(uint8_t button)` {#api-register-joystick-button} - -Set the state of a button, and flush the report. - -#### Arguments {#api-register-joystick-button-arguments} - - - `uint8_t button` - The index of the button to press, from 0 to 31. - ---- - -### `void unregister_joystick_button(uint8_t button)` {#api-unregister-joystick-button} - -Reset the state of a button, and flush the report. - -#### Arguments {#api-unregister-joystick-button-arguments} - - - `uint8_t button` - The index of the button to release, from 0 to 31. - ---- - -### `int16_t joystick_read_axis(uint8_t axis)` {#api-joystick-read-axis} - -Sample and process the analog value of the given axis. - -#### Arguments {#api-joystick-read-axis-arguments} - - - `uint8_t axis` - The axis to read. - -#### Return Value {#api-joystick-read-axis-return} - -A signed 16-bit integer, where 0 is the resting or mid point. - -### `void joystick_set_axis(uint8_t axis, int16_t value)` {#api-joystick-set-axis} - -Set the value of the given axis. - -#### Arguments {#api-joystick-set-axis-arguments} - - - `uint8_t axis` - The axis to set the value of. - - `int16_t value` - The value to set. diff --git a/docs/feature_key_lock.md b/docs/feature_key_lock.md deleted file mode 100644 index 1a0a2dfb60..0000000000 --- a/docs/feature_key_lock.md +++ /dev/null @@ -1,23 +0,0 @@ -# Key Lock - -Sometimes you may find yourself needing to hold down a specific key for a long period of time. Key Lock holds down the next key you press for you. Press it again, and it will be released. - -Let's say you need to type in ALL CAPS for a few sentences. Hit `QK_LOCK`, and then Shift. Now, Shift will be considered held until you tap it again. You can think of Key Lock as Caps Lock, but supercharged. - -## Usage - -First, enable Key Lock by setting `KEY_LOCK_ENABLE = yes` in your `rules.mk`. Then pick a key in your keymap and assign it the keycode `QK_LOCK`. - -## Keycodes - -|Keycode |Description | -|---------|--------------------------------------------------------------| -|`QK_LOCK`|Hold down the next key pressed, until the key is pressed again| - -## Caveats - -Key Lock is only able to hold standard action keys and [One Shot modifier](one_shot_keys) keys (for example, if you have your Shift defined as `OSM(MOD_LSFT)`). -This does not include any of the QMK special functions (except One Shot modifiers), or shifted versions of keys such as `KC_LPRN`. If it's in the [Basic Keycodes](keycodes_basic) list, it can be held. - -Switching layers will not cancel the Key Lock. The Key Lock can be cancelled by calling the `cancel_key_lock()` function. - diff --git a/docs/feature_key_overrides.md b/docs/feature_key_overrides.md deleted file mode 100644 index b9fbd35966..0000000000 --- a/docs/feature_key_overrides.md +++ /dev/null @@ -1,256 +0,0 @@ -# Key Overrides {#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 {#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 {#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-modifier 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 {#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_BSPC, KC_DEL); - -// 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 {#intermediate-difficulty-examples} - -### Media Controls & Screen Brightness {#media-controls-amp-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 {#flexible-macos-friendly-grave-escape} -The [Grave Escape feature](feature_grave_esc) is limited in its configurability and has [bugs when used on macOS](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_GRV)); - -// GUI + esc = ` -const key_override_t grave_esc_override = ko_make_basic(MOD_MASK_GUI, KC_ESC, KC_GRV); - -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 {#advanced-examples} -### Modifiers as Layer Keys {#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 {#keycodes} - -|Keycode |Aliases |Description | -|------------------------|---------|----------------------| -|`QK_KEY_OVERRIDE_TOGGLE`|`KO_TOGG`|Toggle key overrides | -|`QK_KEY_OVERRIDE_ON` |`KO_ON` |Turn on key overrides | -|`QK_KEY_OVERRIDE_OFF` |`KO_OFF` |Turn off key overrides| - -## Reference for `key_override_t` {#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` {#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 {#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 {#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 {#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 {#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 {#difference-to-combos} - -Note that key overrides are very different from [combos](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 interaction with other pressed keys. There are a number of optional settings that can be used to really fine-tune the behavior of each key override as well. Using key overrides also does not delay key input for regular key presses, which inherently happens in combos and may be undesirable. - -## Solution to the problem of flashing modifiers {#neutralize-flashing-modifiers} - -If the programs you use bind an action to taps of modifier keys (e.g. tapping left GUI to bring up the applications menu or tapping left Alt to focus the menu bar), you may find that using key overrides with suppressed mods falsely triggers those actions. To counteract this, you can define a `DUMMY_MOD_NEUTRALIZER_KEYCODE` in `config.h` that will get sent in between the register and unregister events of a suppressed modifier. That way, the programs on your computer will no longer interpret the mod suppression induced by key overrides as a lone tap of a modifier key and will thus not falsely trigger the undesired action. - -Naturally, for this technique to be effective, you must choose a `DUMMY_MOD_NEUTRALIZER_KEYCODE` for which no keyboard shortcuts are bound to. Recommended values are: `KC_RIGHT_CTRL` or `KC_F18`. -Please note that `DUMMY_MOD_NEUTRALIZER_KEYCODE` must be a basic, unmodified, HID keycode so values like `KC_NO`, `KC_TRANSPARENT` or `KC_PIPE` aka `S(KC_BACKSLASH)` are not permitted. - -By default, only left Alt and left GUI are neutralized. If you want to change the list of applicable modifier masks, use the following in your `config.h`: - -```c -#define MODS_TO_NEUTRALIZE { , , ... } -``` - -Examples: - -```c -#define DUMMY_MOD_NEUTRALIZER_KEYCODE KC_RIGHT_CTRL - -// Neutralize left alt and left GUI (Default value) -#define MODS_TO_NEUTRALIZE { MOD_BIT(KC_LEFT_ALT), MOD_BIT(KC_LEFT_GUI) } - -// Neutralize left alt, left GUI, right GUI and left Control+Shift -#define MODS_TO_NEUTRALIZE { MOD_BIT(KC_LEFT_ALT), MOD_BIT(KC_LEFT_GUI), MOD_BIT(KC_RIGHT_GUI), MOD_BIT(KC_LEFT_CTRL)|MOD_BIT(KC_LEFT_SHIFT) } -``` - -::: warning -Do not use `MOD_xxx` constants like `MOD_LSFT` or `MOD_RALT`, since they're 5-bit packed bit-arrays while `MODS_TO_NEUTRALIZE` expects a list of 8-bit packed bit-arrays. Use `MOD_BIT()` or `MOD_MASK_xxx` instead. -::: diff --git a/docs/feature_layers.md b/docs/feature_layers.md index 77b6ef9531..fe9932fadb 100644 --- a/docs/feature_layers.md +++ b/docs/feature_layers.md @@ -27,7 +27,7 @@ For a similar reason, the `layer` argument of `LM()` is also limited to layers 0 |:---------------:|:----------------------:|:------------------------:|:----------------:| | ❌ | ❌ | ❌ | ✅ | -Expanding this would be complicated, at best. Moving to a 32-bit keycode would solve a lot of this, but would double the amount of space that the keymap matrix uses. And it could potentially cause issues, too. If you need to apply modifiers to your tapped keycode, [Tap Dance](feature_tap_dance#example-5-using-tap-dance-for-advanced-mod-tap-and-layer-tap-keys) can be used to accomplish this. +Expanding this would be complicated, at best. Moving to a 32-bit keycode would solve a lot of this, but would double the amount of space that the keymap matrix uses. And it could potentially cause issues, too. If you need to apply modifiers to your tapped keycode, [Tap Dance](features/tap_dance#example-5-using-tap-dance-for-advanced-mod-tap-and-layer-tap-keys) can be used to accomplish this. ## Working with Layers {#working-with-layers} @@ -104,7 +104,7 @@ This runs code every time that the layers get changed. This can be useful for l ### Example `layer_state_set_*` Implementation -This example shows how to set the [RGB Underglow](feature_rgblight) lights based on the layer, using the Planck as an example. +This example shows how to set the [RGB Underglow](features/rgblight) lights based on the layer, using the Planck as an example. ```c layer_state_t layer_state_set_user(layer_state_t state) { diff --git a/docs/feature_leader_key.md b/docs/feature_leader_key.md deleted file mode 100644 index 641c2d7ae5..0000000000 --- a/docs/feature_leader_key.md +++ /dev/null @@ -1,297 +0,0 @@ -# The Leader Key: A New Kind of Modifier {#the-leader-key} - -If you're a Vim user, you probably know what a Leader key is. In contrast to [Combos](feature_combo), the Leader key allows you to hit a *sequence* of up to five keys instead, which triggers some custom functionality once complete. - -## Usage {#usage} - -Add the following to your `rules.mk`: - -```make -LEADER_ENABLE = yes -``` - -Then add the `QK_LEAD` keycode to your keymap. - -## Callbacks {#callbacks} - -These callbacks are invoked when the leader sequence begins and ends. In the latter you can implement your custom functionality based on the contents of the sequence buffer. - -```c -void leader_start_user(void) { - // Do something when the leader key is pressed -} - -void leader_end_user(void) { - if (leader_sequence_one_key(KC_F)) { - // Leader, f => Types the below string - SEND_STRING("QMK is awesome."); - } else if (leader_sequence_two_keys(KC_D, KC_D)) { - // Leader, d, d => Ctrl+A, Ctrl+C - SEND_STRING(SS_LCTL("a") SS_LCTL("c")); - } else if (leader_sequence_three_keys(KC_D, KC_D, KC_S)) { - // Leader, d, d, s => Types the below string - SEND_STRING("https://start.duckduckgo.com\n"); - } else if (leader_sequence_two_keys(KC_A, KC_S)) { - // Leader, a, s => GUI+S - tap_code16(LGUI(KC_S)); - } -} -``` - -## Basic Configuration {#basic-configuration} - -### Timeout {#timeout} - -This is the amount of time you have to complete a sequence once the leader key has been pressed. The default value is 300 milliseconds, but you can change this by adding the following to your `config.h`: - -```c -#define LEADER_TIMEOUT 350 -``` - -### Per-Key Timeout {#per-key-timeout} - -Rather than relying on an incredibly high timeout for long leader key strings or those of us without 200 wpm typing skills, you can enable per-key timing to ensure that each key pressed provides you with more time to finish the sequence. This is incredibly helpful with leader key emulation of tap dance (such as multiple taps of the same key like C, C, C). - -To enable this, add the following to your `config.h`: - -```c -#define LEADER_PER_KEY_TIMING -``` - -After this, it's recommended that you lower your timeout below 300 ms: - -```c -#define LEADER_TIMEOUT 250 -``` - -Now, something like this won't seem impossible to do without a 1000 millisecond timeout: - -```c -if (leader_sequence_three_keys(KC_C, KC_C, KC_C)) { - SEND_STRING("Per key timing is great!!!"); -} -``` - -### Disabling Initial Timeout {#disabling-initial-timeout} - -Sometimes your leader key may be too far away from the rest of the keys in the sequence. Imagine that your leader key is one of your outer top right keys - you may need to reposition your hand just to reach your leader key. This can make typing the entire sequence on time hard difficult if you are able to type most of the sequence fast. For example, if your sequence is `Leader + asd`, typing `asd` fast is very easy once you have your hands in your home row, but starting the sequence in time after moving your hand out of the home row to reach the leader key and back is not. - -To remove the stress this situation produces to your hands, you can disable the timeout just for the leader key. Add the following to your `config.h`: - -```c -#define LEADER_NO_TIMEOUT -``` - -Now, after you hit the leader key, you will have an infinite amount of time to start the rest of the sequence, allowing you to properly position your hands to type the rest of the sequence comfortably. This way you can configure a very short `LEADER_TIMEOUT`, but still have plenty of time to position your hands. - -### Strict Key Processing {#strict-key-processing} - -By default, only the "tap keycode" portions of [Mod-Taps](mod_tap) and [Layer Taps](feature_layers#switching-and-toggling-layers) are added to the sequence buffer. This means if you press eg. `LT(3, KC_A)` as part of a sequence, `KC_A` will be added to the buffer, rather than the entire `LT(3, KC_A)` keycode. - -This gives a more expected behaviour for most users, however you may want to change this. - -To enable this, add the following to your `config.h`: - -```c -#define LEADER_KEY_STRICT_KEY_PROCESSING -``` - -## Example {#example} - -This example will play the Mario "One Up" sound when you hit `QK_LEAD` to start the leader sequence. When the sequence ends, it will play "All Star" if it completes successfully or "Rick Roll" you if it fails (in other words, no sequence matched). - -```c -#ifdef AUDIO_ENABLE -float leader_start_song[][2] = SONG(ONE_UP_SOUND); -float leader_succeed_song[][2] = SONG(ALL_STAR); -float leader_fail_song[][2] = SONG(RICK_ROLL); -#endif - -void leader_start_user(void) { -#ifdef AUDIO_ENABLE - PLAY_SONG(leader_start_song); -#endif -} - -void leader_end_user(void) { - bool did_leader_succeed = false; - - if (leader_sequence_one_key(KC_E)) { - SEND_STRING(SS_LCTL(SS_LSFT("t"))); - did_leader_succeed = true; - } else if (leader_sequence_two_keys(KC_E, KC_D)) { - SEND_STRING(SS_LGUI("r") "cmd\n" SS_LCTL("c")); - did_leader_succeed = true; - } - -#ifdef AUDIO_ENABLE - if (did_leader_succeed) { - PLAY_SONG(leader_succeed_song); - } else { - PLAY_SONG(leader_fail_song); - } -#endif -} -``` - -## Keycodes {#keycodes} - -|Key |Aliases |Description | -|-----------------------|---------|-------------------------| -|`QK_LEADER` |`QK_LEAD`|Begin the leader sequence| - -## API {#api} - -### `void leader_start_user(void)` {#api-leader-start-user} - -User callback, invoked when the leader sequence begins. - ---- - -### `void leader_end_user(void)` {#api-leader-end-user} - -User callback, invoked when the leader sequence ends. - ---- - -### `void leader_start(void)` {#api-leader-start} - -Begin the leader sequence, resetting the buffer and timer. - ---- - -### `void leader_end(void)` {#api-leader-end} - -End the leader sequence. - ---- - -### `bool leader_sequence_active(void)` {#api-leader-sequence-active} - -Whether the leader sequence is active. - ---- - -### `bool leader_sequence_add(uint16_t keycode)` {#api-leader-sequence-add} - -Add the given keycode to the sequence buffer. - -If `LEADER_NO_TIMEOUT` is defined, the timer is reset if the buffer is empty. - -#### Arguments {#api-leader-sequence-add-arguments} - - - `uint16_t keycode` - The keycode to add. - -#### Return Value {#api-leader-sequence-add-return} - -`true` if the keycode was added, `false` if the buffer is full. - ---- - -### `bool leader_sequence_timed_out(void)` {#api-leader-sequence-timed-out} - -Whether the leader sequence has reached the timeout. - -If `LEADER_NO_TIMEOUT` is defined, the buffer must also contain at least one key. - ---- - -### `bool leader_reset_timer(void)` {#api-leader-reset-timer} - -Reset the leader sequence timer. - ---- - -### `bool leader_sequence_one_key(uint16_t kc)` {#api-leader-sequence-one-key} - -Check the sequence buffer for the given keycode. - -#### Arguments {#api-leader-sequence-one-key-arguments} - - - `uint16_t kc` - The keycode to check. - -#### Return Value {#api-leader-sequence-one-key-return} - -`true` if the sequence buffer matches. - ---- - -### `bool leader_sequence_two_keys(uint16_t kc1, uint16_t kc2)` {#api-leader-sequence-two-keys} - -Check the sequence buffer for the given keycodes. - -#### Arguments {#api-leader-sequence-two-keys-arguments} - - - `uint16_t kc1` - The first keycode to check. - - `uint16_t kc2` - The second keycode to check. - -#### Return Value {#api-leader-sequence-two-keys-return} - -`true` if the sequence buffer matches. - ---- - -### `bool leader_sequence_three_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3)` {#api-leader-sequence-three-keys} - -Check the sequence buffer for the given keycodes. - -#### Arguments {#api-leader-sequence-three-keys-arguments} - - - `uint16_t kc1` - The first keycode to check. - - `uint16_t kc2` - The second keycode to check. - - `uint16_t kc3` - The third keycode to check. - -#### Return Value {#api-leader-sequence-three-keys-return} - -`true` if the sequence buffer matches. - ---- - -### `bool leader_sequence_four_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3, uint16_t kc4)` {#api-leader-sequence-four-keys} - -Check the sequence buffer for the given keycodes. - -#### Arguments {#api-leader-sequence-four-keys-arguments} - - - `uint16_t kc1` - The first keycode to check. - - `uint16_t kc2` - The second keycode to check. - - `uint16_t kc3` - The third keycode to check. - - `uint16_t kc4` - The fourth keycode to check. - -#### Return Value {#api-leader-sequence-four-keys-return} - -`true` if the sequence buffer matches. - ---- - -### `bool leader_sequence_five_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3, uint16_t kc4, uint16_t kc5)` {#api-leader-sequence-five-keys} - -Check the sequence buffer for the given keycodes. - -#### Arguments {#api-leader-sequence-five-keys-arguments} - - - `uint16_t kc1` - The first keycode to check. - - `uint16_t kc2` - The second keycode to check. - - `uint16_t kc3` - The third keycode to check. - - `uint16_t kc4` - The fourth keycode to check. - - `uint16_t kc5` - The fifth keycode to check. - -#### Return Value {#api-leader-sequence-five-keys-return} - -`true` if the sequence buffer matches. diff --git a/docs/feature_led_indicators.md b/docs/feature_led_indicators.md deleted file mode 100644 index e28c222e76..0000000000 --- a/docs/feature_led_indicators.md +++ /dev/null @@ -1,127 +0,0 @@ -# LED Indicators - -::: tip -LED indicators on split keyboards will require state information synced to the slave half (e.g. `#define SPLIT_LED_STATE_ENABLE`). See [data sync options](feature_split_keyboard#data-sync-options) for more details. -::: - -QMK provides methods to read 5 of the LEDs defined in the HID spec: - -* Num Lock -* Caps Lock -* Scroll Lock -* Compose -* Kana - -There are three ways to get the lock LED state: -* Configuration options in `config.h` -* Implement `led_update_*` function -* Call `led_t host_keyboard_led_state()` - -::: warning -The `host_keyboard_led_state()` may reflect an updated state before `led_update_user()` is called. -::: - -Two deprecated functions that provide the LED state as `uint8_t`: - -* `uint8_t led_set_user(uint8_t usb_led)` -* `uint8_t host_keyboard_leds()` - -## Configuration Options - -To configure the indicators, `#define` these in your `config.h`: - -|Define |Default |Description | -|---------------------|-------------|-------------------------------------------| -|`LED_NUM_LOCK_PIN` |*Not defined*|The pin that controls the `Num Lock` LED | -|`LED_CAPS_LOCK_PIN` |*Not defined*|The pin that controls the `Caps Lock` LED | -|`LED_SCROLL_LOCK_PIN`|*Not defined*|The pin that controls the `Scroll Lock` LED| -|`LED_COMPOSE_PIN` |*Not defined*|The pin that controls the `Compose` LED | -|`LED_KANA_PIN` |*Not defined*|The pin that controls the `Kana` LED | -|`LED_PIN_ON_STATE` |`1` |The state of the indicator pins when the LED is "on" - `1` for high, `0` for low| - -Unless you are designing your own keyboard, you generally should not need to change the above config options. - -## LED update function - -When the configuration options do not provide enough flexibility, the following callbacks allow custom control of the LED behavior. These functions will be called when one of those 5 LEDs changes state: - -* Keyboard/revision: `bool led_update_kb(led_t led_state)` -* Keymap: `bool led_update_user(led_t led_state)` - -Both receives LED state as a struct parameter. Returning `true` in `led_update_user()` will allow the keyboard level code in `led_update_kb()` to run as well. Returning `false` will override the keyboard level code, depending on how the keyboard level function is set up. - -::: tip -This boolean return type of `led_update_user` allows for overriding keyboard LED controls, and is thus recommended over the void `led_set_user` function. -::: - -### Example of keyboard LED update implementation - -This is a template indicator function that can be implemented on keyboard level code: - -```c -bool led_update_kb(led_t led_state) { - bool res = led_update_user(led_state); - if(res) { - // gpio_write_pin sets the pin high for 1 and low for 0. - // In this example the pins are inverted, setting - // it low/0 turns it on, and high/1 turns the LED off. - // This behavior depends on whether the LED is between the pin - // and VCC or the pin and GND. - gpio_write_pin(B0, !led_state.num_lock); - gpio_write_pin(B1, !led_state.caps_lock); - gpio_write_pin(B2, !led_state.scroll_lock); - gpio_write_pin(B3, !led_state.compose); - gpio_write_pin(B4, !led_state.kana); - } - return res; -} -``` - -### Example of user LED update implementation - -This is an incomplete example will play a sound if Caps Lock is turned on or off. It returns `true` to allow keyboard LED function to maintain their state. - -```c -#ifdef AUDIO_ENABLE - float caps_on[][2] = SONG(CAPS_LOCK_ON_SOUND); - float caps_off[][2] = SONG(CAPS_LOCK_OFF_SOUND); -#endif - -bool led_update_user(led_t led_state) { - #ifdef AUDIO_ENABLE - static uint8_t caps_state = 0; - if (caps_state != led_state.caps_lock) { - led_state.caps_lock ? PLAY_SONG(caps_on) : PLAY_SONG(caps_off); - caps_state = led_state.caps_lock; - } - #endif - return true; -} -``` - -## Host keyboard LED state - -The `host_keyboard_led_state()` function will report the LED state returned from the host computer as `led_t`. This is useful for reading the LED state outside `led_update_*`. For example, you can get the boolean state of Caps Lock from the host with: - -```c -bool caps = host_keyboard_led_state().caps_lock; -``` - -## `led_update_ports()` - -This function writes the LED state to the actual hardware. Call it manually -from your `led_update_*()` callbacks to modify the handling of the standard -keyboard LEDs. -For example when repurposing a standard LED indicator as layer indicator. - -## Setting Physical LED State - -Some keyboard implementations provide convenient methods for setting the state of the physical LEDs. - -### Ergodox Boards - -The Ergodox implementations provide `ergodox_right_led_1`/`2`/`3_on`/`off()` to turn individual LEDs on or off, as well as `ergodox_right_led_on`/`off(uint8_t led)` to turn them on or off by their index. - -In addition, it is possible to specify the brightness level of all LEDs with `ergodox_led_all_set(uint8_t n)`; of individual LEDs with `ergodox_right_led_1`/`2`/`3_set(uint8_t n)`; or by index with `ergodox_right_led_set(uint8_t led, uint8_t n)`. - -Ergodox boards also define `LED_BRIGHTNESS_LO` for the lowest brightness and `LED_BRIGHTNESS_HI` for the highest brightness (which is the default). diff --git a/docs/feature_led_matrix.md b/docs/feature_led_matrix.md deleted file mode 100644 index 78afb36d2c..0000000000 --- a/docs/feature_led_matrix.md +++ /dev/null @@ -1,455 +0,0 @@ -# LED Matrix Lighting {#led-matrix-lighting} - -This feature allows you to use LED matrices driven by external drivers. It hooks into the backlight system so you can use the same keycodes as backlighting to control it. - -If you want to use RGB LED's you should use the [RGB Matrix Subsystem](feature_rgb_matrix) instead. - -## Driver configuration {#driver-configuration} ---- -### IS31FL3731 {#is31fl3731} - -There is basic support for addressable LED matrix lighting with the I2C IS31FL3731 LED controller. To enable it, add this to your `rules.mk`: - -```make -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`: - -| Variable | Description | Default | -|----------|-------------|---------| -| `IS31FL3731_I2C_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 | -| `IS31FL3731_I2C_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 | -| `LED_MATRIX_LED_COUNT` | (Required) How many LED lights are present across all drivers | | -| `IS31FL3731_I2C_ADDRESS_1` | (Required) Address for the first LED driver | | -| `IS31FL3731_I2C_ADDRESS_2` | (Optional) Address for the second LED driver | | -| `IS31FL3731_I2C_ADDRESS_3` | (Optional) Address for the third LED driver | | -| `IS31FL3731_I2C_ADDRESS_4` | (Optional) Address for the fourth LED driver | | - -Here is an example using 2 drivers. - -```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) -// The address will vary depending on your wiring: -// 00 AD <-> GND -// 01 AD <-> SCL -// 10 AD <-> SDA -// 11 AD <-> VCC -// ADDR represents A1:A0 of the 7-bit address. -// The result is: 0b11101(ADDR) -#define IS31FL3731_I2C_ADDRESS_1 IS31FL3731_I2C_ADDRESS_GND -#define IS31FL3731_I2C_ADDRESS_2 IS31FL3731_I2C_ADDRESS_SDA - -#define LED_DRIVER_1_LED_TOTAL 25 -#define LED_DRIVER_2_LED_TOTAL 24 -#define LED_MATRIX_LED_COUNT (LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL) -``` - -::: warning -Note the parentheses, this is so when `LED_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL)` will give very different results than `rand() % LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL`. -::: - -For split keyboards using `LED_MATRIX_SPLIT` with an LED driver, you can either have the same driver address or different driver addresses. If using different addresses, use `IS31FL3731_I2C_ADDRESS_1` for one and `IS31FL3731_I2C_ADDRESS_2` for the other one. Then, in `g_is31fl3731_leds`, fill out the correct driver index (0 or 1). If using one address, use `IS31FL3731_I2C_ADDRESS_1` for both, and use index 0 for `g_is31fl3731_leds`. - -Define these arrays listing all the LEDs in your `.c`: - -```c -const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT] = { -/* Refer to IS31 manual for these locations - * driver - * | LED address - * | | */ - { 0, C1_1 }, - { 0, C1_15 }, - // ... -} -``` - -Where `Cx_y` is the location of the LED in the matrix defined by [the datasheet](https://www.issi.com/WW/pdf/31FL3731.pdf) and the header file `drivers/led/issi/is31fl3731-mono.h`. The `driver` is the index of the driver you defined in your `config.h` (`0`, `1`, `2`, or `3` ). - ---- -### IS31FLCOMMON {#is31flcommon} - -There is basic support for addressable LED matrix lighting with a selection of I2C ISSI Lumissil LED controllers through a shared common driver. To enable it, add this to your `rules.mk`: - -```makefile -LED_MATRIX_ENABLE = yes -LED_MATRIX_DRIVER = -``` - -Where `` is the applicable LED driver chip as below - -| Driver Name | Data Sheet | Capability | -|-------------|------------|------------| -| `IS31FL3742A` | [datasheet](https://www.lumissil.com/assets/pdf/core/IS31FL3742A_DS.pdf) | 180 LED, 30x6 Matrix | -| `IS31FL3743A` | [datasheet](https://www.lumissil.com/assets/pdf/core/IS31FL3743A_DS.pdf) | 198 LED, 18x11 Matrix | -| `IS31FL3745` | [datasheet](https://www.lumissil.com/assets/pdf/core/IS31FL3745_DS.pdf) | 144 LED, 18x8 Matrix | -| `IS31FL3746A` | [datasheet](https://www.lumissil.com/assets/pdf/core/IS31FL3746A_DS.pdf) | 72 LED, 18x4 Matrix | - -You can use between 1 and 4 IC's. Do not specify `DRIVER_ADDR_` define for IC's if not present on your keyboard. The `DRIVER_ADDR_1` default assumes that all Address pins on the controller have been connected to GND. Drivers that have SYNC functionality have the default settings to disable if 1 driver. If more than 1 drivers then `DRIVER_ADDR_1` will be set to Master and the remaiing ones set to Slave. - -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 | -| `LED_MATRIX_LED_COUNT` | (Required) How many LED lights are present across all drivers | | -| `DRIVER_ADDR_1` | (Optional) Address for the first LED driver | | -| `DRIVER_ADDR_` | (Required) Address for the additional LED drivers | | -| `ISSI_SSR_` | (Optional) Configuration for the Spread Spectrum Register | | -| `ISSI_CONFIGURATION` | (Optional) Configuration for the Configuration Register | | -| `ISSI_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF | -| `ISSI_PULLDOWNUP` | (Optional) Configuration for the Pull Up & Pull Down Register | | -| `ISSI_TEMP` | (Optional) Configuration for the Tempature Register | | -| `ISSI_PWM_ENABLE` | (Optional) Configuration for the PWM Enable Register | | -| `ISSI_PWM_SET` | (Optional) Configuration for the PWM Setting Register | | -| `ISSI_SCAL_LED ` | (Optional) Configuration for the LEDs Scaling Registers | 0xFF | -| `ISSI_MANUAL_SCALING` | (Optional) If you wish to configure the Scaling Registers manually | | - - -Defaults - -| Variable | IS31FL3742A | IS31FL3743A | IS31FL3745 | IS31FL3746 | -|----------|-------------|-------------|------------|------------| -| `DRIVER_ADDR_1` | 0b0110000 | 0b0100000 | 0b0100000 | 0b1100000 | -| `ISSI_SSR_1` | 0x00 | 0x00 / 0x60 | 0x00 / 0xC0 | 0x00 | -| `ISSI_SSR_<2-4>` | 0x00 | 0x40 | 0x80 | 0x00 | -| `ISSI_CONFIGURATION` | 0x31 | 0x01 | 0x31 | 0x01 | -| `ISSI_PULLDOWNUP` | 0x55 | 0x33 | 0x33 | 0x33 | -| `ISSI_TEMP` | N/A | 0x00 | 0x00 | 0x00 | -| `ISSI_PWM_ENABLE` | N/A | N/A | N/A | 0x00 | -| `ISSI_PWM_SET` | 0x00 | N/A | N/A | 0x00 | - -Here is an example using 2 drivers. - -```c -#define DRIVER_ADDR_2 0b0100001 - -#define DRIVER_1_LED_TOTAL 66 -#define DRIVER_2_LED_TOTAL 42 -#define LED_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL) -``` -::: warning -Note the parentheses, this is so when `LED_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (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 4 drivers are supported, but it would be trivial to support for more. Note that using a combination of different drivers is not supported. All drivers must be of the same model. - -Define these arrays listing all the LEDs in your `.c`: - -```c -const is31_led PROGMEM g_is31_leds[LED_MATRIX_LED_COUNT] = { -/* Refer to IS31 manual for these locations - * driver - * | LED address - * | | */ - { 0, SW1_CS1 }, - { 0, SW1_CS2 }, - // ... -} -``` - -Where `CSx_SWx` is the location of the LED in the matrix defined by the datasheet. The `driver` is the index of the driver you defined in your `config.h` (`0`, `1`, `2`, or `3` for now). - -`ISSI_MANUAL_SCALING` is used to override the Scaling for individual LED's. By default they will be set as per `ISSI_SCAL_LED`. In `config.h` set how many LED's you want to manually set scaling for. -Eg `#define ISSI_MANUAL_SCALING 3` - -Then Define the array listing all the LEDs you want to override in your `.c`: - -```c -const is31_led PROGMEM g_is31_scaling[ISSI_MANUAL_SCALING] = { -/* LED Index - * | Scaling - * | | */ - {5, 120}, - {9, 120}, - .... -} -``` - -Where LED Index is the position of the LED in the `g_is31_leds` array. The `scaling` value between 0 and 255 to be written to the Scaling Register. - ---- - -## Common Configuration {#common-configuration} - -From this point forward the configuration is the same for all the drivers. The `led_config_t` struct provides a key electrical matrix to led index lookup table, what the physical position of each LED is on the board, and what type of key or usage the LED if the LED represents. Here is a brief example: - -```c -led_config_t g_led_config = { { - // Key Matrix to LED Index - { 5, NO_LED, NO_LED, 0 }, - { NO_LED, NO_LED, NO_LED, NO_LED }, - { 4, NO_LED, NO_LED, 1 }, - { 3, NO_LED, NO_LED, 2 } -}, { - // LED Index to Physical Position - { 188, 16 }, { 187, 48 }, { 149, 64 }, { 112, 64 }, { 37, 48 }, { 38, 16 } -}, { - // LED Index to Flag - 1, 4, 4, 4, 4, 1 -} }; -``` - -The first part, `// Key Matrix to LED Index`, tells the system what key this LED represents by using the key's electrical matrix row & col. The second part, `// LED Index to Physical Position` represents the LED's physical `{ x, y }` position on the keyboard. The default expected range of values for `{ x, y }` is the inclusive range `{ 0..224, 0..64 }`. This default expected range is due to effects that calculate the center of the keyboard for their animations. The easiest way to calculate these positions is imagine your keyboard is a grid, and the top left of the keyboard represents `{ x, y }` coordinate `{ 0, 0 }` and the bottom right of your keyboard represents `{ 224, 64 }`. Using this as a basis, you can use the following formula to calculate the physical position: - -```c -x = 224 / (NUMBER_OF_COLS - 1) * COL_POSITION -y = 64 / (NUMBER_OF_ROWS - 1) * ROW_POSITION -``` - -Where NUMBER_OF_COLS, NUMBER_OF_ROWS, COL_POSITION, & ROW_POSITION are all based on the physical layout of your keyboard, not the electrical layout. - -As mentioned earlier, the center of the keyboard by default is expected to be `{ 112, 32 }`, but this can be changed if you want to more accurately calculate the LED's physical `{ x, y }` positions. Keyboard designers can implement `#define LED_MATRIX_CENTER { 112, 32 }` in their config.h file with the new center point of the keyboard, or where they want it to be allowing more possibilities for the `{ x, y }` values. Do note that the maximum value for x or y is 255, and the recommended maximum is 224 as this gives animations runoff room before they reset. - -`// LED Index to Flag` is a bitmask, whether or not a certain LEDs is of a certain type. It is recommended that LEDs are set to only 1 type. - -## Flags {#flags} - -|Define |Value |Description | -|----------------------------|------|-------------------------------------------------| -|`HAS_FLAGS(bits, flags)` |*n/a* |Evaluates to `true` if `bits` has all `flags` set| -|`HAS_ANY_FLAGS(bits, flags)`|*n/a* |Evaluates to `true` if `bits` has any `flags` set| -|`LED_FLAG_NONE` |`0x00`|If this LED has no flags | -|`LED_FLAG_ALL` |`0xFF`|If this LED has all flags | -|`LED_FLAG_MODIFIER` |`0x01`|If the LED is on a modifier key | -|`LED_FLAG_KEYLIGHT` |`0x04`|If the LED is for key backlight | -|`LED_FLAG_INDICATOR` |`0x08`|If the LED is for keyboard state indication | - -## Keycodes {#keycodes} - -|Key |Aliases |Description | -|-------------------------------|---------|-----------------------------------| -|`QK_LED_MATRIX_ON` |`LM_ON` |Turn on LED Matrix | -|`QK_LED_MATRIX_OFF` |`LM_OFF` |Turn off LED Matrix | -|`QK_LED_MATRIX_TOGGLE` |`LM_TOGG`|Toggle LED Matrix on or off | -|`QK_LED_MATRIX_MODE_NEXT` |`LM_NEXT`|Cycle through animations | -|`QK_LED_MATRIX_MODE_PREVIOUS` |`LM_PREV`|Cycle through animations in reverse| -|`QK_LED_MATRIX_BRIGHTNESS_UP` |`LM_BRIU`|Increase the brightness level | -|`QK_LED_MATRIX_BRIGHTNESS_DOWN`|`LM_BRID`|Decrease the brightness level | -|`QK_LED_MATRIX_SPEED_UP` |`LM_SPDU`|Increase the animation speed | -|`QK_LED_MATRIX_SPEED_DOWN` |`LM_SPDD`|Decrease the animation speed | - -## LED Matrix Effects {#led-matrix-effects} - -These are the effects that are currently available: - -```c -enum led_matrix_effects { - LED_MATRIX_NONE = 0, - LED_MATRIX_SOLID = 1, // Static single val, no speed support - LED_MATRIX_ALPHAS_MODS, // Static dual val, speed is val for LEDs marked as modifiers - LED_MATRIX_BREATHING, // Cycling brightness animation - LED_MATRIX_BAND, // Band fading brightness scrolling left to right - LED_MATRIX_BAND_PINWHEEL, // 3 blade spinning pinwheel fades brightness - LED_MATRIX_BAND_SPIRAL, // Spinning spiral fades brightness - LED_MATRIX_CYCLE_LEFT_RIGHT, // Full gradient scrolling left to right - LED_MATRIX_CYCLE_UP_DOWN, // Full gradient scrolling top to bottom - LED_MATRIX_CYCLE_OUT_IN, // Full gradient scrolling out to in - LED_MATRIX_DUAL_BEACON, // Full gradient spinning around center of keyboard - LED_MATRIX_SOLID_REACTIVE_SIMPLE, // Pulses keys hit then fades out - LED_MATRIX_SOLID_REACTIVE_WIDE, // Value pulses near a single key hit then fades out - LED_MATRIX_SOLID_REACTIVE_MULTIWIDE, // Value pulses near multiple key hits then fades out - LED_MATRIX_SOLID_REACTIVE_CROSS, // Value pulses the same column and row of a single key hit then fades out - LED_MATRIX_SOLID_REACTIVE_MULTICROSS, // Value pulses the same column and row of multiple key hits then fades out - LED_MATRIX_SOLID_REACTIVE_NEXUS, // Value pulses away on the same column and row of a single key hit then fades out - LED_MATRIX_SOLID_REACTIVE_MULTINEXUS, // Value pulses away on the same column and row of multiple key hits then fades out - LED_MATRIX_SOLID_SPLASH, // Value pulses away from a single key hit then fades out - LED_MATRIX_SOLID_MULTISPLASH, // Value pulses away from multiple key hits then fades out - LED_MATRIX_WAVE_LEFT_RIGHT, // Sine wave scrolling from left to right - LED_MATRIX_WAVE_UP_DOWN, // Sine wave scrolling from up to down - LED_MATRIX_EFFECT_MAX -}; -``` - -You can enable a single effect by defining `ENABLE_[EFFECT_NAME]` in your `config.h`: - - -|Define |Description | -|-------------------------------------------------------|----------------------------------------------| -|`#define ENABLE_LED_MATRIX_ALPHAS_MODS` |Enables `LED_MATRIX_ALPHAS_MODS` | -|`#define ENABLE_LED_MATRIX_BREATHING` |Enables `LED_MATRIX_BREATHING` | -|`#define ENABLE_LED_MATRIX_BAND` |Enables `LED_MATRIX_BAND` | -|`#define ENABLE_LED_MATRIX_BAND_PINWHEEL` |Enables `LED_MATRIX_BAND_PINWHEEL` | -|`#define ENABLE_LED_MATRIX_BAND_SPIRAL` |Enables `LED_MATRIX_BAND_SPIRAL` | -|`#define ENABLE_LED_MATRIX_CYCLE_LEFT_RIGHT` |Enables `LED_MATRIX_CYCLE_LEFT_RIGHT` | -|`#define ENABLE_LED_MATRIX_CYCLE_UP_DOWN` |Enables `LED_MATRIX_CYCLE_UP_DOWN` | -|`#define ENABLE_LED_MATRIX_CYCLE_OUT_IN` |Enables `LED_MATRIX_CYCLE_OUT_IN` | -|`#define ENABLE_LED_MATRIX_DUAL_BEACON` |Enables `LED_MATRIX_DUAL_BEACON` | -|`#define ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT` |Enables `LED_MATRIX_WAVE_LEFT_RIGHT` | -|`#define ENABLE_LED_MATRIX_WAVE_UP_DOWN` |Enables `LED_MATRIX_WAVE_UP_DOWN` | - -|Reactive Defines |Description | -|-------------------------------------------------------|----------------------------------------------| -|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE` |Enables `LED_MATRIX_SOLID_REACTIVE_SIMPLE` | -|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE` |Enables `LED_MATRIX_SOLID_REACTIVE_WIDE` | -|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE` |Enables `LED_MATRIX_SOLID_REACTIVE_MULTIWIDE` | -|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS` |Enables `LED_MATRIX_SOLID_REACTIVE_CROSS` | -|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS` |Enables `LED_MATRIX_SOLID_REACTIVE_MULTICROSS`| -|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS` |Enables `LED_MATRIX_SOLID_REACTIVE_NEXUS` | -|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS` |Enables `LED_MATRIX_SOLID_REACTIVE_MULTINEXUS`| -|`#define ENABLE_LED_MATRIX_SOLID_SPLASH` |Enables `LED_MATRIX_SOLID_SPLASH` | -|`#define ENABLE_LED_MATRIX_SOLID_MULTISPLASH` |Enables `LED_MATRIX_SOLID_MULTISPLASH` | - -::: tip -These modes introduce additional logic that can increase firmware size. -::: - -## Custom LED Matrix Effects {#custom-led-matrix-effects} - -By setting `LED_MATRIX_CUSTOM_USER` (and/or `LED_MATRIX_CUSTOM_KB`) in `rules.mk`, new effects can be defined directly from userspace, without having to edit any QMK core files. - -To declare new effects, create a new `led_matrix_user/kb.inc` that looks something like this: - -`led_matrix_user.inc` should go in the root of the keymap directory. -`led_matrix_kb.inc` should go in the root of the keyboard directory. - -To use custom effects in your code, simply prepend `LED_MATRIX_CUSTOM_` to the effect name specified in `LED_MATRIX_EFFECT()`. For example, an effect declared as `LED_MATRIX_EFFECT(my_cool_effect)` would be referenced with: - -```c -led_matrix_mode(led_MATRIX_CUSTOM_my_cool_effect); -``` - -```c -// !!! DO NOT ADD #pragma once !!! // - -// Step 1. -// Declare custom effects using the LED_MATRIX_EFFECT macro -// (note the lack of semicolon after the macro!) -LED_MATRIX_EFFECT(my_cool_effect) -LED_MATRIX_EFFECT(my_cool_effect2) - -// Step 2. -// Define effects inside the `LED_MATRIX_CUSTOM_EFFECT_IMPLS` ifdef block -#ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS - -// e.g: A simple effect, self-contained within a single method -static bool my_cool_effect(effect_params_t* params) { - LED_MATRIX_USE_LIMITS(led_min, led_max); - for (uint8_t i = led_min; i < led_max; i++) { - led_matrix_set_value(i, 0xFF); - } - return led_matrix_check_finished_leds(led_max); -} - -// e.g: A more complex effect, relying on external methods and state, with -// dedicated init and run methods -static uint8_t some_global_state; -static void my_cool_effect2_complex_init(effect_params_t* params) { - some_global_state = 1; -} -static bool my_cool_effect2_complex_run(effect_params_t* params) { - LED_MATRIX_USE_LIMITS(led_min, led_max); - for (uint8_t i = led_min; i < led_max; i++) { - led_matrix_set_value(i, some_global_state++); - } - return led_matrix_check_finished_leds(led_max); -} -static bool my_cool_effect2(effect_params_t* params) { - if (params->init) my_cool_effect2_complex_init(params); - return my_cool_effect2_complex_run(params); -} - -#endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS -``` - -For inspiration and examples, check out the built-in effects under `quantum/led_matrix/animations/`. - - -## Additional `config.h` Options {#additional-configh-options} - -```c -#define LED_MATRIX_KEYRELEASES // reactive effects respond to keyreleases (instead of keypresses) -#define LED_MATRIX_TIMEOUT 0 // number of milliseconds to wait until led automatically turns off -#define LED_MATRIX_SLEEP // turn off effects when suspended -#define LED_MATRIX_LED_PROCESS_LIMIT (LED_MATRIX_LED_COUNT + 4) / 5 // limits the number of LEDs to process in an animation per task run (increases keyboard responsiveness) -#define LED_MATRIX_LED_FLUSH_LIMIT 16 // limits in milliseconds how frequently an animation will update the LEDs. 16 (16ms) is equivalent to limiting to 60fps (increases keyboard responsiveness) -#define LED_MATRIX_MAXIMUM_BRIGHTNESS 255 // limits maximum brightness of LEDs -#define LED_MATRIX_DEFAULT_ON true // Sets the default enabled state, if none has been set -#define LED_MATRIX_DEFAULT_MODE LED_MATRIX_SOLID // Sets the default mode, if none has been set -#define LED_MATRIX_DEFAULT_VAL LED_MATRIX_MAXIMUM_BRIGHTNESS // Sets the default brightness value, if none has been set -#define LED_MATRIX_DEFAULT_SPD 127 // Sets the default animation speed, if none has been set -#define LED_MATRIX_DEFAULT_FLAGS LED_FLAG_ALL // Sets the default LED flags, if none has been set -#define LED_MATRIX_SPLIT { X, Y } // (Optional) For split keyboards, the number of LEDs connected on each half. X = left, Y = Right. - // If reactive effects are enabled, you also will want to enable SPLIT_TRANSPORT_MIRROR -``` - -## EEPROM storage {#eeprom-storage} - -The EEPROM for it is currently shared with the RGB Matrix system (it's generally assumed only one feature would be used at a time). - -### Direct Operation {#direct-operation} -|Function |Description | -|--------------------------------------------|-------------| -|`led_matrix_set_value_all(v)` |Set all of the LEDs to the given value, where `v` is between 0 and 255 (not written to EEPROM) | -|`led_matrix_set_value(index, v)` |Set a single LED to the given value, where `v` is between 0 and 255, and `index` is between 0 and `LED_MATRIX_LED_COUNT` (not written to EEPROM) | - -### Disable/Enable Effects {#disable-enable-effects} -|Function |Description | -|--------------------------------------------|-------------| -|`led_matrix_toggle()` |Toggle effect range LEDs between on and off | -|`led_matrix_toggle_noeeprom()` |Toggle effect range LEDs between on and off (not written to EEPROM) | -|`led_matrix_enable()` |Turn effect range LEDs on, based on their previous state | -|`led_matrix_enable_noeeprom()` |Turn effect range LEDs on, based on their previous state (not written to EEPROM) | -|`led_matrix_disable()` |Turn effect range LEDs off, based on their previous state | -|`led_matrix_disable_noeeprom()` |Turn effect range LEDs off, based on their previous state (not written to EEPROM) | - -### Change Effect Mode {#change-effect-mode} -|Function |Description | -|--------------------------------------------|-------------| -|`led_matrix_mode(mode)` |Set the mode, if LED animations are enabled | -|`led_matrix_mode_noeeprom(mode)` |Set the mode, if LED animations are enabled (not written to EEPROM) | -|`led_matrix_step()` |Change the mode to the next LED animation in the list of enabled LED animations | -|`led_matrix_step_noeeprom()` |Change the mode to the next LED animation in the list of enabled LED animations (not written to EEPROM) | -|`led_matrix_step_reverse()` |Change the mode to the previous LED animation in the list of enabled LED animations | -|`led_matrix_step_reverse_noeeprom()` |Change the mode to the previous LED animation in the list of enabled LED animations (not written to EEPROM) | -|`led_matrix_increase_speed()` |Increase the speed of the animations | -|`led_matrix_increase_speed_noeeprom()` |Increase the speed of the animations (not written to EEPROM) | -|`led_matrix_decrease_speed()` |Decrease the speed of the animations | -|`led_matrix_decrease_speed_noeeprom()` |Decrease the speed of the animations (not written to EEPROM) | -|`led_matrix_set_speed(speed)` |Set the speed of the animations to the given value where `speed` is between 0 and 255 | -|`led_matrix_set_speed_noeeprom(speed)` |Set the speed of the animations to the given value where `speed` is between 0 and 255 (not written to EEPROM) | - -### Change Value {#change-value} -|Function |Description | -|--------------------------------------------|-------------| -|`led_matrix_increase_val()` |Increase the value for effect range LEDs. This wraps around at maximum value | -|`led_matrix_increase_val_noeeprom()` |Increase the value for effect range LEDs. This wraps around at maximum value (not written to EEPROM) | -|`led_matrix_decrease_val()` |Decrease the value for effect range LEDs. This wraps around at minimum value | -|`led_matrix_decrease_val_noeeprom()` |Decrease the value for effect range LEDs. This wraps around at minimum value (not written to EEPROM) | - -### Query Current Status {#query-current-status} -|Function |Description | -|---------------------------------|---------------------------| -|`led_matrix_is_enabled()` |Gets current on/off status | -|`led_matrix_get_mode()` |Gets current mode | -|`led_matrix_get_val()` |Gets current val | -|`led_matrix_get_speed()` |Gets current speed | -|`led_matrix_get_suspend_state()` |Gets current suspend state | - -## Callbacks {#callbacks} - -### Indicators {#indicators} - -If you want to set custom indicators, such as an LED for Caps Lock, or layer indication, then you can use the `led_matrix_indicators_kb` function on the keyboard level source file, or `led_matrix_indicators_user` function in the user `keymap.c`. -```c -bool led_matrix_indicators_kb(void) { - if (!led_matrix_indicators_user()) { - return false; - } - led_matrix_set_value(index, value); - return true; -} -``` - -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. This includes a special macro to help make this easier to use: `LED_MATRIX_INDICATOR_SET_VALUE(i, v)`. - -```c -void led_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) { - LED_MATRIX_INDICATOR_SET_VALUE(index, value); - return false; -} -``` diff --git a/docs/feature_macros.md b/docs/feature_macros.md index d5a830c0ef..055bb3ff8f 100644 --- a/docs/feature_macros.md +++ b/docs/feature_macros.md @@ -86,7 +86,7 @@ All objects have one required key: `action`. This tells QMK what the object does Only basic keycodes (prefixed by `KC_`) are supported. Do not include the `KC_` prefix when listing keycodes. * `beep` - * Play a bell if the keyboard has [audio enabled](feature_audio). + * Play a bell if the keyboard has [audio enabled](features/audio). * Example: `{"action": "beep"}` * `delay` * Pause macro playback. Duration is specified in milliseconds (ms). @@ -108,7 +108,7 @@ Only basic keycodes (prefixed by `KC_`) are supported. Do not include the `KC_` ### `SEND_STRING()` & `process_record_user` -See also: [Send String](feature_send_string) +See also: [Send String](features/send_string) Sometimes you want a key to type out words or phrases. For the most common situations, we've provided `SEND_STRING()`, which will type out a string (i.e. a sequence of characters) for you. All ASCII characters that are easily translatable to a keycode are supported (e.g. `qmk 123\n\t`). diff --git a/docs/feature_midi.md b/docs/feature_midi.md deleted file mode 100644 index 89c6af0508..0000000000 --- a/docs/feature_midi.md +++ /dev/null @@ -1,264 +0,0 @@ -# MIDI - -## Usage - -First, enable MIDI by adding the following to your `rules.mk`: - -```make -MIDI_ENABLE = yes -``` - -There are two MIDI systems in QMK: basic and advanced. With basic MIDI you will only be able to send Note On and Note Off messages using the note keycodes, meaning that keycodes like `MI_OCTU` and `MI_OCTD` will not work. Advanced MIDI allows you to do things like octave shifts, channel changes, velocity changes, modulation, and more. - -### Caveats - -MIDI requires 2 USB endpoints and as such may not work on some hardware such as V-USB controllers. - -### Basic MIDI - -To enable basic MIDI, add the following to your `config.h`: - -```c -#define MIDI_BASIC -``` - -### Advanced MIDI - -To enable advanced MIDI, add the following to your `config.h`: - -```c -#define MIDI_ADVANCED -``` - -#### Sending Control Change (CC) Messages - -If you're aiming to emulate the features of something like a Launchpad or other MIDI controller you'll need to access the internal MIDI device directly. - -Because there are so many possible CC messages, not all of them are implemented as keycodes. Additionally, you might need to provide more than just two values that you would get from a keycode (pressed and released) - for example, the analog values from a fader or a potentiometer. So, you will need to implement [custom keycodes](feature_macros) if you want to use them in your keymap directly using `process_record_user()`. - - -For reference of all the possible control code numbers see [MIDI Specification](#midi-specification) - -#### Example code for using Generic On Off Switches as per MIDI Specification. -```c -#include QMK_KEYBOARD_H - -extern MidiDevice midi_device; - -// MIDI CC codes for generic on/off switches (80, 81, 82, 83) -// Off: 0-63 -// On: 64-127 - -#define MIDI_CC_OFF 0 -#define MIDI_CC_ON 127 - -enum custom_keycodes { - MIDI_CC80 = SAFE_RANGE, -}; - -bool process_record_user(uint16_t keycode, keyrecord_t *record) { - switch (keycode) { - case MIDI_CC80: - if (record->event.pressed) { - midi_send_cc(&midi_device, midi_config.channel, 80, MIDI_CC_ON); - } else { - midi_send_cc(&midi_device, midi_config.channel, 80, MIDI_CC_OFF); - } - return true; - } - return true; -}; - -const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { - LAYOUT( - // ... - MIDI_CC80, - // ... - ) -}; -``` - -### Keycodes - -|Keycode |Aliases |Description | -|-------------------------------|------------------|---------------------------------| -|`QK_MIDI_ON` |`MI_ON` |Turn MIDI on | -|`QK_MIDI_OFF` |`MI_OFF` |Turn MIDI off | -|`QK_MIDI_TOGGLE` |`MI_TOGG` |Toggle MIDI enabled | -|`QK_MIDI_NOTE_C_0` |`MI_C` |C octave 0 | -|`QK_MIDI_NOTE_C_SHARP_0` |`MI_Cs`, `MI_Db` |C♯/D♭ octave 0 | -|`QK_MIDI_NOTE_D_0` |`MI_D` |D octave 0 | -|`QK_MIDI_NOTE_D_SHARP_0` |`MI_Ds`, `MI_Eb` |D♯/E♭ octave 0 | -|`QK_MIDI_NOTE_E_0` |`MI_E` |E octave 0 | -|`QK_MIDI_NOTE_F_0` |`MI_F` |F octave 0 | -|`QK_MIDI_NOTE_F_SHARP_0` |`MI_Fs`, `MI_Gb` |F♯/G♭ octave 0 | -|`QK_MIDI_NOTE_G_0` |`MI_G` |G octave 0 | -|`QK_MIDI_NOTE_G_SHARP_0` |`MI_Gs`, `MI_Ab` |G♯/A♭ octave 0 | -|`QK_MIDI_NOTE_A_0` |`MI_A` |A octave 0 | -|`QK_MIDI_NOTE_A_SHARP_0` |`MI_As`, `MI_Bb` |A♯/B♭ octave 0 | -|`QK_MIDI_NOTE_B_0` |`MI_B` |B octave 0 | -|`QK_MIDI_NOTE_C_1` |`MI_C1` |C octave 1 | -|`QK_MIDI_NOTE_C_SHARP_1` |`MI_Cs1`, `MI_Db1`|C♯/D♭ octave 1 | -|`QK_MIDI_NOTE_D_1` |`MI_D1` |D octave 1 | -|`QK_MIDI_NOTE_D_SHARP_1` |`MI_Ds1`, `MI_Eb1`|D♯/E♭ octave 1 | -|`QK_MIDI_NOTE_E_1` |`MI_E1` |E octave 1 | -|`QK_MIDI_NOTE_F_1` |`MI_F1` |F octave 1 | -|`QK_MIDI_NOTE_F_SHARP_1` |`MI_Fs1`, `MI_Gb1`|F♯/G♭ octave 1 | -|`QK_MIDI_NOTE_G_1` |`MI_G1` |G octave 1 | -|`QK_MIDI_NOTE_G_SHARP_1` |`MI_Gs1`, `MI_Ab1`|G♯/A♭ octave 1 | -|`QK_MIDI_NOTE_A_1` |`MI_A1` |A octave 1 | -|`QK_MIDI_NOTE_A_SHARP_1` |`MI_As1`, `MI_Bb1`|A♯/B♭ octave 1 | -|`QK_MIDI_NOTE_B_1` |`MI_B1` |B octave 1 | -|`QK_MIDI_NOTE_C_2` |`MI_C2` |C octave 2 | -|`QK_MIDI_NOTE_C_SHARP_2` |`MI_Cs2`, `MI_Db2`|C♯/D♭ octave 2 | -|`QK_MIDI_NOTE_D_2` |`MI_D2` |D octave 2 | -|`QK_MIDI_NOTE_D_SHARP_2` |`MI_Ds2`, `MI_Eb2`|D♯/E♭ octave 2 | -|`QK_MIDI_NOTE_E_2` |`MI_E2` |E octave 2 | -|`QK_MIDI_NOTE_F_2` |`MI_F2` |F octave 2 | -|`QK_MIDI_NOTE_F_SHARP_2` |`MI_Fs2`, `MI_Gb2`|F♯/G♭ octave 2 | -|`QK_MIDI_NOTE_G_2` |`MI_G2` |G octave 2 | -|`QK_MIDI_NOTE_G_SHARP_2` |`MI_Gs2`, `MI_Ab2`|G♯/A♭ octave 2 | -|`QK_MIDI_NOTE_A_2` |`MI_A2` |A octave 2 | -|`QK_MIDI_NOTE_A_SHARP_2` |`MI_As2`, `MI_Bb2`|A♯/B♭ octave 2 | -|`QK_MIDI_NOTE_B_2` |`MI_B2` |B octave 2 | -|`QK_MIDI_NOTE_C_3` |`MI_C3` |C octave 3 | -|`QK_MIDI_NOTE_C_SHARP_3` |`MI_Cs3`, `MI_Db3`|C♯/D♭ octave 3 | -|`QK_MIDI_NOTE_D_3` |`MI_D3` |D octave 3 | -|`QK_MIDI_NOTE_D_SHARP_3` |`MI_Ds3`, `MI_Eb3`|D♯/E♭ octave 3 | -|`QK_MIDI_NOTE_E_3` |`MI_E3` |E octave 3 | -|`QK_MIDI_NOTE_F_3` |`MI_F3` |F octave 3 | -|`QK_MIDI_NOTE_F_SHARP_3` |`MI_Fs3`, `MI_Gb3`|F♯/G♭ octave 3 | -|`QK_MIDI_NOTE_G_3` |`MI_G3` |G octave 3 | -|`QK_MIDI_NOTE_G_SHARP_3` |`MI_Gs3`, `MI_Ab3`|G♯/A♭ octave 3 | -|`QK_MIDI_NOTE_A_3` |`MI_A3` |A octave 3 | -|`QK_MIDI_NOTE_A_SHARP_3` |`MI_As3`, `MI_Bb3`|A♯/B♭ octave 3 | -|`QK_MIDI_NOTE_B_3` |`MI_B3` |B octave 3 | -|`QK_MIDI_NOTE_C_4` |`MI_C4` |C octave 4 | -|`QK_MIDI_NOTE_C_SHARP_4` |`MI_Cs4`, `MI_Db4`|C♯/D♭ octave 4 | -|`QK_MIDI_NOTE_D_4` |`MI_D4` |D octave 4 | -|`QK_MIDI_NOTE_D_SHARP_4` |`MI_Ds4`, `MI_Eb4`|D♯/E♭ octave 4 | -|`QK_MIDI_NOTE_E_4` |`MI_E4` |E octave 4 | -|`QK_MIDI_NOTE_F_4` |`MI_F4` |F octave 4 | -|`QK_MIDI_NOTE_F_SHARP_4` |`MI_Fs4`, `MI_Gb4`|F♯/G♭ octave 4 | -|`QK_MIDI_NOTE_G_4` |`MI_G4` |G octave 4 | -|`QK_MIDI_NOTE_G_SHARP_4` |`MI_Gs4`, `MI_Ab4`|G♯/A♭ octave 4 | -|`QK_MIDI_NOTE_A_4` |`MI_A4` |A octave 4 | -|`QK_MIDI_NOTE_A_SHARP_4` |`MI_As4`, `MI_Bb4`|A♯/B♭ octave 4 | -|`QK_MIDI_NOTE_B_4` |`MI_B4` |B octave 4 | -|`QK_MIDI_NOTE_C_5` |`MI_C5` |C octave 5 | -|`QK_MIDI_NOTE_C_SHARP_5` |`MI_Cs5`, `MI_Db5`|C♯/D♭ octave 5 | -|`QK_MIDI_NOTE_D_5` |`MI_D5` |D octave 5 | -|`QK_MIDI_NOTE_D_SHARP_5` |`MI_Ds5`, `MI_Eb5`|D♯/E♭ octave 5 | -|`QK_MIDI_NOTE_E_5` |`MI_E5` |E octave 5 | -|`QK_MIDI_NOTE_F_5` |`MI_F5` |F octave 5 | -|`QK_MIDI_NOTE_F_SHARP_5` |`MI_Fs5`, `MI_Gb5`|F♯/G♭ octave 5 | -|`QK_MIDI_NOTE_G_5` |`MI_G5` |G octave 5 | -|`QK_MIDI_NOTE_G_SHARP_5` |`MI_Gs5`, `MI_Ab5`|G♯/A♭ octave 5 | -|`QK_MIDI_NOTE_A_5` |`MI_A5` |A octave 5 | -|`QK_MIDI_NOTE_A_SHARP_5` |`MI_As5`, `MI_Bb5`|A♯/B♭ octave 5 | -|`QK_MIDI_NOTE_B_5` |`MI_B5` |B octave 5 | -|`QK_MIDI_OCTAVE_N2` |`MI_OCN2` |Set octave to -2 | -|`QK_MIDI_OCTAVE_N1` |`MI_OCN1` |Set octave to -1 | -|`QK_MIDI_OCTAVE_0` |`MI_OC0` |Set octave to 0 | -|`QK_MIDI_OCTAVE_1` |`MI_OC1` |Set octave to 1 | -|`QK_MIDI_OCTAVE_2` |`MI_OC2` |Set octave to 2 | -|`QK_MIDI_OCTAVE_3` |`MI_OC3` |Set octave to 3 | -|`QK_MIDI_OCTAVE_4` |`MI_OC4` |Set octave to 4 | -|`QK_MIDI_OCTAVE_5` |`MI_OC5` |Set octave to 5 | -|`QK_MIDI_OCTAVE_6` |`MI_OC6` |Set octave to 6 | -|`QK_MIDI_OCTAVE_7` |`MI_OC7` |Set octave to 7 | -|`QK_MIDI_OCTAVE_DOWN` |`MI_OCTD` |Move down an octave | -|`QK_MIDI_OCTAVE_UP` |`MI_OCTU` |Move up an octave | -|`QK_MIDI_TRANSPOSE_N6` |`MI_TRN6` |Set transposition to -6 semitones| -|`QK_MIDI_TRANSPOSE_N5` |`MI_TRN5` |Set transposition to -5 semitones| -|`QK_MIDI_TRANSPOSE_N4` |`MI_TRN4` |Set transposition to -4 semitones| -|`QK_MIDI_TRANSPOSE_N3` |`MI_TRN3` |Set transposition to -3 semitones| -|`QK_MIDI_TRANSPOSE_N2` |`MI_TRN2` |Set transposition to -2 semitones| -|`QK_MIDI_TRANSPOSE_N1` |`MI_TRN1` |Set transposition to -1 semitone | -|`QK_MIDI_TRANSPOSE_0` |`MI_TR0` |No transposition | -|`QK_MIDI_TRANSPOSE_1` |`MI_TR1` |Set transposition to +1 semitone | -|`QK_MIDI_TRANSPOSE_2` |`MI_TR2` |Set transposition to +2 semitones| -|`QK_MIDI_TRANSPOSE_3` |`MI_TR3` |Set transposition to +3 semitones| -|`QK_MIDI_TRANSPOSE_4` |`MI_TR4` |Set transposition to +4 semitones| -|`QK_MIDI_TRANSPOSE_5` |`MI_TR5` |Set transposition to +5 semitones| -|`QK_MIDI_TRANSPOSE_6` |`MI_TR6` |Set transposition to +6 semitones| -|`QK_MIDI_TRANSPOSE_DOWN` |`MI_TRSD` |Decrease transposition | -|`QK_MIDI_TRANSPOSE_UP` |`MI_TRSU` |Increase transposition | -|`QK_MIDI_VELOCITY_0` |`MI_VL0` |Set velocity to 0 | -|`QK_MIDI_VELOCITY_1` |`MI_VL1` |Set velocity to 12 | -|`QK_MIDI_VELOCITY_2` |`MI_VL2` |Set velocity to 25 | -|`QK_MIDI_VELOCITY_3` |`MI_VL3` |Set velocity to 38 | -|`QK_MIDI_VELOCITY_4` |`MI_VL4` |Set velocity to 51 | -|`QK_MIDI_VELOCITY_5` |`MI_VL5` |Set velocity to 64 | -|`QK_MIDI_VELOCITY_6` |`MI_VL6` |Set velocity to 76 | -|`QK_MIDI_VELOCITY_7` |`MI_VL7` |Set velocity to 89 | -|`QK_MIDI_VELOCITY_8` |`MI_VL8` |Set velocity to 102 | -|`QK_MIDI_VELOCITY_9` |`MI_VL9` |Set velocity to 114 | -|`QK_MIDI_VELOCITY_10` |`MI_VL10` |Set velocity to 127 | -|`QK_MIDI_VELOCITY_DOWN` |`MI_VELD` |Decrease velocity | -|`QK_MIDI_VELOCITY_UP` |`MI_VELU` |Increase velocity | -|`QK_MIDI_CHANNEL_1` |`MI_CH1` |Set channel to 1 | -|`QK_MIDI_CHANNEL_2` |`MI_CH2` |Set channel to 2 | -|`QK_MIDI_CHANNEL_3` |`MI_CH3` |Set channel to 3 | -|`QK_MIDI_CHANNEL_4` |`MI_CH4` |Set channel to 4 | -|`QK_MIDI_CHANNEL_5` |`MI_CH5` |Set channel to 5 | -|`QK_MIDI_CHANNEL_6` |`MI_CH6` |Set channel to 6 | -|`QK_MIDI_CHANNEL_7` |`MI_CH7` |Set channel to 7 | -|`QK_MIDI_CHANNEL_8` |`MI_CH8` |Set channel to 8 | -|`QK_MIDI_CHANNEL_9` |`MI_CH9` |Set channel to 9 | -|`QK_MIDI_CHANNEL_10` |`MI_CH10` |Set channel to 10 | -|`QK_MIDI_CHANNEL_11` |`MI_CH11` |Set channel to 11 | -|`QK_MIDI_CHANNEL_12` |`MI_CH12` |Set channel to 12 | -|`QK_MIDI_CHANNEL_13` |`MI_CH13` |Set channel to 13 | -|`QK_MIDI_CHANNEL_14` |`MI_CH14` |Set channel to 14 | -|`QK_MIDI_CHANNEL_15` |`MI_CH15` |Set channel to 15 | -|`QK_MIDI_CHANNEL_16` |`MI_CH16` |Set channel to 16 | -|`QK_MIDI_CHANNEL_DOWN` |`MI_CHND` |Decrease channel | -|`QK_MIDI_CHANNEL_UP` |`MI_CHNU` |Increase channel | -|`QK_MIDI_ALL_NOTES_OFF` |`MI_AOFF` |Stop all notes | -|`QK_MIDI_SUSTAIN` |`MI_SUST` |Sustain | -|`QK_MIDI_PORTAMENTO` |`MI_PORT` |Portmento | -|`QK_MIDI_SOSTENUTO` |`MI_SOST` |Sostenuto | -|`QK_MIDI_SOFT` |`MI_SOFT` |Soft Pedal | -|`QK_MIDI_LEGATO` |`MI_LEG` |Legato | -|`QK_MIDI_MODULATION` |`MI_MOD` |Modulation | -|`QK_MIDI_MODULATION_SPEED_DOWN`|`MI_MODD` |Decrease modulation speed | -|`QK_MIDI_MODULATION_SPEED_UP` |`MI_MODU` |Increase modulation speed | -|`QK_MIDI_PITCH_BEND_DOWN` |`MI_BNDD` |Bend pitch down | -|`QK_MIDI_PITCH_BEND_UP` |`MI_BNDU` |Bend pitch up | - -### Configuration - -Certain values are stored in the `midi_config` struct. This configuration is not persisted to EEPROM. By default, these values are: - -|Configuration |Value|Comments | -|-------------------|-----|-----------------------| -|Octave |`4` |Corresponds to `MI_OC2`| -|Transposition |`0` | | -|Velocity |`127`| | -|Channel |`0` | | -|Modulation Interval|`8` | | - -For the above, the `MI_C` keycode will produce a C3 (note number 48), and so on. - -### References -#### MIDI Specification - - * [MIDI.org](https://www.midi.org/specifications-old/item/table-1-summary-of-midi-message) - * [CMU MIDI Programmer's Reference](https://www.cs.cmu.edu/~music/cmsip/readings/MIDI%20tutorial%20for%20programmers.html) -#### QMK C Files - - * `quantum/process_keycode/process_midi.c` - * `quantum/quantum_keycodes.h` - * `quantum/midi/midi.h` - * `quantum/midi/midi.c` - * `quantum/midi/qmk_midi.c` - * `quantum/midi/midi_device.h` - - diff --git a/docs/feature_mouse_keys.md b/docs/feature_mouse_keys.md deleted file mode 100644 index 240f6bf9be..0000000000 --- a/docs/feature_mouse_keys.md +++ /dev/null @@ -1,208 +0,0 @@ -# Mouse keys - -Mouse keys is a feature that allows you to emulate a mouse using your keyboard. You can move the pointer at different speeds, press 5 buttons and scroll in 8 directions. - -## Adding mouse keys to your keyboard - -To use mouse keys, you must at least enable mouse keys support and map mouse actions to keys on your keyboard. - -### Enabling mouse keys - -To enable mouse keys, add the following line to your keymap’s `rules.mk`: - -```c -MOUSEKEY_ENABLE = yes -``` - -### Mapping mouse actions - -In your keymap you can use the following keycodes to map key presses to mouse actions: - -|Key |Aliases |Description | -|----------------|---------|-----------------| -|`KC_MS_UP` |`KC_MS_U`|Move cursor up | -|`KC_MS_DOWN` |`KC_MS_D`|Move cursor down | -|`KC_MS_LEFT` |`KC_MS_L`|Move cursor left | -|`KC_MS_RIGHT` |`KC_MS_R`|Move cursor right| -|`KC_MS_BTN1` |`KC_BTN1`|Press button 1 | -|`KC_MS_BTN2` |`KC_BTN2`|Press button 2 | -|`KC_MS_BTN3` |`KC_BTN3`|Press button 3 | -|`KC_MS_BTN4` |`KC_BTN4`|Press button 4 | -|`KC_MS_BTN5` |`KC_BTN5`|Press button 5 | -|`KC_MS_BTN6` |`KC_BTN6`|Press button 6 | -|`KC_MS_BTN7` |`KC_BTN7`|Press button 7 | -|`KC_MS_BTN8` |`KC_BTN8`|Press button 8 | -|`KC_MS_WH_UP` |`KC_WH_U`|Move wheel up | -|`KC_MS_WH_DOWN` |`KC_WH_D`|Move wheel down | -|`KC_MS_WH_LEFT` |`KC_WH_L`|Move wheel left | -|`KC_MS_WH_RIGHT`|`KC_WH_R`|Move wheel right | -|`KC_MS_ACCEL0` |`KC_ACL0`|Set speed to 0 | -|`KC_MS_ACCEL1` |`KC_ACL1`|Set speed to 1 | -|`KC_MS_ACCEL2` |`KC_ACL2`|Set speed to 2 | - -## Configuring mouse keys - -Mouse keys supports three different modes to move the cursor: - -* **Accelerated (default):** Holding movement keys accelerates the cursor until it reaches its maximum speed. -* **Kinetic:** Holding movement keys accelerates the cursor with its speed following a quadratic curve until it reaches its maximum speed. -* **Constant:** Holding movement keys moves the cursor at constant speeds. -* **Combined:** Holding movement keys accelerates the cursor until it reaches its maximum speed, but holding acceleration and movement keys simultaneously moves the cursor at constant speeds. -* **Inertia:** Cursor accelerates when key held, and decelerates after key release. Tracks X and Y velocity separately for more nuanced movements. Applies to cursor only, not scrolling. - -The same principle applies to scrolling, in most modes. - -Configuration options that are times, intervals or delays are given in milliseconds. Scroll speed is given as multiples of the default scroll step. For example, a scroll speed of 8 means that each scroll action covers 8 times the length of the default scroll step as defined by your operating system or application. - -### Accelerated mode - -This is the default mode. You can adjust the cursor and scrolling acceleration using the following settings in your keymap’s `config.h` file: - -|Define |Default|Description | -|----------------------------|-------|---------------------------------------------------------| -|`MOUSEKEY_DELAY` |10 |Delay between pressing a movement key and cursor movement| -|`MOUSEKEY_INTERVAL` |20 |Time between cursor movements in milliseconds | -|`MOUSEKEY_MOVE_DELTA` |8 |Step size | -|`MOUSEKEY_MAX_SPEED` |10 |Maximum cursor speed at which acceleration stops | -|`MOUSEKEY_TIME_TO_MAX` |30 |Time until maximum cursor speed is reached | -|`MOUSEKEY_WHEEL_DELAY` |10 |Delay between pressing a wheel key and wheel movement | -|`MOUSEKEY_WHEEL_INTERVAL` |80 |Time between wheel movements | -|`MOUSEKEY_WHEEL_DELTA` |1 |Wheel movement step size | -|`MOUSEKEY_WHEEL_MAX_SPEED` |8 |Maximum number of scroll steps per scroll action | -|`MOUSEKEY_WHEEL_TIME_TO_MAX`|40 |Time until maximum scroll speed is reached | - -Tips: - -* Setting `MOUSEKEY_DELAY` too low makes the cursor unresponsive. Setting it too high makes small movements difficult. -* For smoother cursor movements, lower the value of `MOUSEKEY_INTERVAL`. If the refresh rate of your display is 60Hz, you could set it to `16` (1/60). As this raises the cursor speed significantly, you may want to lower `MOUSEKEY_MAX_SPEED`. -* Setting `MOUSEKEY_TIME_TO_MAX` or `MOUSEKEY_WHEEL_TIME_TO_MAX` to `0` will disable acceleration for the cursor or scrolling respectively. This way you can make one of them constant while keeping the other accelerated, which is not possible in constant speed mode. -* Setting `MOUSEKEY_WHEEL_INTERVAL` too low will make scrolling too fast. Setting it too high will make scrolling too slow when the wheel key is held down. - -Cursor acceleration uses the same algorithm as the X Window System MouseKeysAccel feature. You can read more about it [on Wikipedia](https://en.wikipedia.org/wiki/Mouse_keys). - -### Kinetic Mode - -This is an extension of the accelerated mode. The kinetic mode uses a quadratic curve on the cursor speed which allows precise movements at the beginning and allows to cover large distances by increasing cursor speed quickly thereafter. You can adjust the cursor and scrolling acceleration using the following settings in your keymap’s `config.h` file: - -|Define |Default |Description | -|--------------------------------------|---------|---------------------------------------------------------------| -|`MK_KINETIC_SPEED` |undefined|Enable kinetic mode | -|`MOUSEKEY_DELAY` |5 |Delay between pressing a movement key and cursor movement | -|`MOUSEKEY_INTERVAL` |10 |Time between cursor movements in milliseconds | -|`MOUSEKEY_MOVE_DELTA` |16 |Step size for accelerating from initial to base speed | -|`MOUSEKEY_INITIAL_SPEED` |100 |Initial speed of the cursor in pixel per second | -|`MOUSEKEY_BASE_SPEED` |5000 |Maximum cursor speed at which acceleration stops | -|`MOUSEKEY_DECELERATED_SPEED` |400 |Decelerated cursor speed | -|`MOUSEKEY_ACCELERATED_SPEED` |3000 |Accelerated cursor speed | -|`MOUSEKEY_WHEEL_INITIAL_MOVEMENTS` |16 |Initial number of movements of the mouse wheel | -|`MOUSEKEY_WHEEL_BASE_MOVEMENTS` |32 |Maximum number of movements at which acceleration stops | -|`MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS`|48 |Accelerated wheel movements | -|`MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS`|8 |Decelerated wheel movements | - -Tips: - -* The smoothness of the cursor movement depends on the `MOUSEKEY_INTERVAL` setting. The shorter the interval is set the smoother the movement will be. Setting the value too low makes the cursor unresponsive. Lower settings are possible if the micro processor is fast enough. For example: At an interval of `8` milliseconds, `125` movements per second will be initiated. With a base speed of `1000` each movement will move the cursor by `8` pixels. -* Mouse wheel movements are implemented differently from cursor movements. While it's okay for the cursor to move multiple pixels at once for the mouse wheel this would lead to jerky movements. Instead, the mouse wheel operates at step size `1`. Setting mouse wheel speed is done by adjusting the number of wheel movements per second. - -### Constant mode - -In this mode you can define multiple different speeds for both the cursor and the mouse wheel. There is no acceleration. `KC_ACL0`, `KC_ACL1` and `KC_ACL2` change the cursor and scroll speed to their respective setting. - -You can choose whether speed selection is momentary or tap-to-select: - -* **Momentary:** The chosen speed is only active while you hold the respective key. When the key is raised, mouse keys returns to the unmodified speed. -* **Tap-to-select:** The chosen speed is activated when you press the respective key and remains active even after the key has been raised. The default speed is that of `KC_ACL1`. There is no unmodified speed. - -The default speeds from slowest to fastest are as follows: - -* **Momentary:** `KC_ACL0` < `KC_ACL1` < *unmodified* < `KC_ACL2` -* **Tap-to-select:** `KC_ACL0` < `KC_ACL1` < `KC_ACL2` - -To use constant speed mode, you must at least define `MK_3_SPEED` in your keymap’s `config.h` file: - -```c -#define MK_3_SPEED -``` - -To enable momentary mode, also define `MK_MOMENTARY_ACCEL`: - -```c -#define MK_MOMENTARY_ACCEL -``` - -Use the following settings if you want to adjust cursor movement or scrolling: - -|Define |Default |Description | -|---------------------|-------------|-------------------------------------------| -|`MK_3_SPEED` |*Not defined*|Enable constant cursor speeds | -|`MK_MOMENTARY_ACCEL` |*Not defined*|Enable momentary speed selection | -|`MK_C_OFFSET_UNMOD` |16 |Cursor offset per movement (unmodified) | -|`MK_C_INTERVAL_UNMOD`|16 |Time between cursor movements (unmodified) | -|`MK_C_OFFSET_0` |1 |Cursor offset per movement (`KC_ACL0`) | -|`MK_C_INTERVAL_0` |32 |Time between cursor movements (`KC_ACL0`) | -|`MK_C_OFFSET_1` |4 |Cursor offset per movement (`KC_ACL1`) | -|`MK_C_INTERVAL_1` |16 |Time between cursor movements (`KC_ACL1`) | -|`MK_C_OFFSET_2` |32 |Cursor offset per movement (`KC_ACL2`) | -|`MK_C_INTERVAL_2` |16 |Time between cursor movements (`KC_ACL2`) | -|`MK_W_OFFSET_UNMOD` |1 |Scroll steps per scroll action (unmodified)| -|`MK_W_INTERVAL_UNMOD`|40 |Time between scroll steps (unmodified) | -|`MK_W_OFFSET_0` |1 |Scroll steps per scroll action (`KC_ACL0`) | -|`MK_W_INTERVAL_0` |360 |Time between scroll steps (`KC_ACL0`) | -|`MK_W_OFFSET_1` |1 |Scroll steps per scroll action (`KC_ACL1`) | -|`MK_W_INTERVAL_1` |120 |Time between scroll steps (`KC_ACL1`) | -|`MK_W_OFFSET_2` |1 |Scroll steps per scroll action (`KC_ACL2`) | -|`MK_W_INTERVAL_2` |20 |Time between scroll steps (`KC_ACL2`) | - -### Combined mode - -This mode functions like **Accelerated** mode, however, you can hold `KC_ACL0`, `KC_ACL1` and `KC_ACL2` -to momentarily (while held) set the cursor and scroll speeds to constant speeds. When no acceleration -keys are held, this mode is identical to **Accelerated** mode, and can be modified using all of the -relevant settings. - -* **KC_ACL0:** This acceleration sets your cursor to the slowest possible speed. This is useful for very -small and detailed movements of the cursor. -* **KC_ACL1:** This acceleration sets your cursor to half the maximum (user defined) speed. -* **KC_ACL2:** This acceleration sets your cursor to the maximum (computer defined) speed. This is -useful for moving the cursor large distances without much accuracy. - -To use combined speed mode, you must at least define `MK_COMBINED` in your keymap’s `config.h` file: - -```c -#define MK_COMBINED -``` - -### Inertia mode - -This mode provides smooth motion, like sliding on ice. The cursor accelerates -along a quadratic curve while a key is held, then glides to a stop after the -key is released. Vertical and horizontal movements are tracked independently, -so the cursor can move in many directions and make curves. - -Cannot be used at the same time as Kinetic mode, Constant mode, or Combined mode. - -Recommended settings in your keymap’s `config.h` file: - -|Define |Default |Description | -|----------------------------|---------|-----------------------------------------------------------| -|`MOUSEKEY_INERTIA` |undefined|Enable Inertia mode | -|`MOUSEKEY_DELAY` |150 |Delay between pressing a movement key and cursor movement | -|`MOUSEKEY_INTERVAL` |16 |Time between cursor movements in milliseconds (16 = 60fps) | -|`MOUSEKEY_MAX_SPEED` |32 |Maximum cursor speed at which acceleration stops | -|`MOUSEKEY_TIME_TO_MAX` |32 |Number of frames until maximum cursor speed is reached | -|`MOUSEKEY_FRICTION` |24 |How quickly the cursor stops after releasing a key | -|`MOUSEKEY_MOVE_DELTA` |1 |How much to move on first frame (1 strongly recommended) | - -Tips: - -* Set `MOUSEKEY_DELAY` to roughly the same value as your host computer's key repeat delay, in ms. Recommended values are 100 to 300. -* Set `MOUSEKEY_INTERVAL` to a value of 1000 / your monitor's FPS. For 60 FPS, 1000/60 = 16. -* Set `MOUSEKEY_MAX_SPEED` based on your screen resolution and refresh rate, like Width / FPS. For example, 1920 pixels / 60 FPS = 32 pixels per frame. -* Set `MOUSEKEY_TIME_TO_MAX` to a value of approximately FPS / 2, to make it reach full speed in half a second (or so). -* Set `MOUSEKEY_FRICTION` to something between 1 and 255. Lower makes the cursor glide longer. Values from 8 to 40 are the most effective. -* Keep `MOUSEKEY_MOVE_DELTA` at 1. This allows precise movements before the gliding effect starts. -* Mouse wheel options are the same as the default accelerated mode, and do not use inertia. - -## Use with PS/2 Mouse and Pointing Device - -Mouse keys button state is shared with [PS/2 mouse](feature_ps2_mouse) and [pointing device](feature_pointing_device) 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 deleted file mode 100644 index cb7a2f13f3..0000000000 --- a/docs/feature_oled_driver.md +++ /dev/null @@ -1,499 +0,0 @@ -# OLED Driver - -## Supported Hardware - -OLED modules using SSD1306, SH1106 or SH1107 driver ICs, communicating over I2C or SPI. -Tested combinations: - -|IC |Size |Platform|Notes | -|---------|-------|--------|------------------------| -|SSD1306 |128x32 |AVR |Primary support | -|SSD1306 |128x64 |AVR |Verified working | -|SSD1306 |128x32 |Arm | | -|SSD1306 |128x64 |Arm |Verified working | -|SH1106 |128x64 |AVR |No scrolling | -|SH1107 |64x128 |AVR |No scrolling | -|SH1107 |64x128 |Arm |No scrolling | -|SH1107 |128x128|Arm |No scrolling | - -Hardware configurations using Arm-based microcontrollers or different sizes of OLED modules may be compatible, but are untested. - -## Usage - -To enable the OLED feature, there are two steps. First, when compiling your keyboard, you'll need to add the following to your `rules.mk`: - -```make -OLED_ENABLE = yes -``` - -## OLED type - -|OLED Driver |Supported Device | -|-------------------|------------------------------------| -|`ssd1306` (default)|For both SSD1306, SH1106, and SH1107| - -e.g. -```make -OLED_DRIVER = ssd1306 -``` - -|OLED Transport | | -|---------------|------------------------------------------------| -|`i2c` (default)| Uses I2C for communication with the OLED panel | -|`spi` | Uses SPI for communication with the OLED panel | - -e.g. -```make -OLED_TRANSPORT = i2c -``` - -Then in your `keymap.c` file, implement the OLED task call. This example assumes your keymap has three layers named `_QWERTY`, `_FN` and `_ADJ`: - -```c -#ifdef OLED_ENABLE -bool oled_task_user(void) { - // Host Keyboard Layer Status - oled_write_P(PSTR("Layer: "), false); - - switch (get_highest_layer(layer_state)) { - case _QWERTY: - oled_write_P(PSTR("Default\n"), false); - break; - case _FN: - oled_write_P(PSTR("FN\n"), false); - break; - case _ADJ: - oled_write_P(PSTR("ADJ\n"), false); - break; - default: - // Or use the write_ln shortcut over adding '\n' to the end of your string - oled_write_ln_P(PSTR("Undefined"), false); - } - - // Host Keyboard LED Status - led_t led_state = host_keyboard_led_state(); - oled_write_P(led_state.num_lock ? PSTR("NUM ") : PSTR(" "), false); - oled_write_P(led_state.caps_lock ? PSTR("CAP ") : PSTR(" "), false); - oled_write_P(led_state.scroll_lock ? PSTR("SCR ") : PSTR(" "), false); - - return false; -} -#endif -``` - -## Logo Example - -In the default font, certain ranges of characters are reserved for a QMK logo. To render this logo to the OLED 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 - }; - - oled_write_P(qmk_logo, false); -} - -bool oled_task_user(void) { - render_logo(); - return false; -} -``` - -::: tip -The default font file is located at `drivers/oled/glcdfont.c` and its location can be overwritten with the `OLED_FONT_H` configuration option. Font file content can be edited with external tools such as [Helix Font Editor](https://helixfonteditor.netlify.app/) and [Logo Editor](https://joric.github.io/qle/). -::: - -## Buffer Read Example -For some purposes, you may need to read the current state of the OLED display -buffer. The `oled_read_raw` function can be used to safely read bytes from the -buffer. - -In this example, calling `fade_display` in the `oled_task_user` function will -slowly fade away whatever is on the screen by turning random pixels black 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 - oled_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 = oled_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) { - oled_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 OLED 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 OLED_ENABLE -oled_rotation_t oled_init_user(oled_rotation_t rotation) { - if (!is_keyboard_master()) { - return OLED_ROTATION_180; // flips the display 180 degrees if offhand - } - - return rotation; -} - -bool oled_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 - oled_scroll_left(); // Turns on scrolling - } - return false; -} -#endif -``` - -Render a message before booting into bootloader mode. -```c -void oled_render_boot(bool bootloader) { - oled_clear(); - for (int i = 0; i < 16; i++) { - oled_set_cursor(0, i); - if (bootloader) { - oled_write_P(PSTR("Awaiting New Firmware "), false); - } else { - oled_write_P(PSTR("Rebooting "), false); - } - } - - oled_render_dirty(true); -} - -bool shutdown_user(bool jump_to_bootloader) { - oled_render_boot(jump_to_bootloader); -} - -``` - -## Basic Configuration - -These configuration options should be placed in `config.h`. Example: -```c -#define OLED_BRIGHTNESS 128 -``` - -|Define |Default |Description | -|---------------------------|-------------------------------|---------------------------------------------------------------------------------------------------------------------| -|`OLED_BRIGHTNESS` |`255` |The default brightness level of the OLED, from 0 to 255. | -|`OLED_COLUMN_OFFSET` |`0` |Shift output to the right this many pixels.
Useful for 128x64 displays centered on a 132x64 SH1106 IC. | -|`OLED_DISPLAY_CLOCK` |`0x80` |Set the display clock divide ratio/oscillator frequency. | -|`OLED_FONT_H` |`"glcdfont.c"` |The font code file to use for custom fonts | -|`OLED_FONT_START` |`0` |The starting character index for custom fonts | -|`OLED_FONT_END` |`223` |The ending character index for custom fonts | -|`OLED_FONT_WIDTH` |`6` |The font width | -|`OLED_FONT_HEIGHT` |`8` |The font height (untested) | -|`OLED_IC` |`OLED_IC_SSD1306` |Set to `OLED_IC_SH1106` or `OLED_IC_SH1107` if the corresponding controller chip is used. | -|`OLED_FADE_OUT` |*Not defined* |Enables fade out animation. Use together with `OLED_TIMEOUT`. | -|`OLED_FADE_OUT_INTERVAL` |`0` |The speed of fade out animation, from 0 to 15. Larger values are slower. | -|`OLED_SCROLL_TIMEOUT` |`0` |Scrolls the OLED screen after 0ms of OLED inactivity. Helps reduce OLED Burn-in. Set to 0 to disable. | -|`OLED_SCROLL_TIMEOUT_RIGHT`|*Not defined* |Scroll timeout direction is right when defined, left when undefined. | -|`OLED_TIMEOUT` |`60000` |Turns off the OLED screen after 60000ms of screen update inactivity. Helps reduce OLED Burn-in. Set to 0 to disable. | -|`OLED_UPDATE_INTERVAL` |`0` (`50` for split keyboards) |Set the time interval for updating the OLED display in ms. This will improve the matrix scan rate. | -|`OLED_UPDATE_PROCESS_LIMIT`|`1` |Set the number of dirty blocks to render per loop. Increasing may degrade performance. | - -### I2C Configuration -|Define |Default |Description | -|---------------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------| -|`OLED_DISPLAY_ADDRESS` |`0x3C` |The i2c address of the OLED Display | - -### SPI Configuration - -|Define |Default |Description | -|---------------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------| -|`OLED_DC_PIN` | Required |The pin used for the DC connection of the OLED Display. | -|`OLED_CS_PIN` | Required |The pin used for the CS connection of the OLED Display. | -|`OLED_RST_PIN` | *Not defined* |The pin used for the RST connection of the OLED Display (may be left undefined if the RST pin is not connected). | -|`OLED_SPI_MODE` |`3` (default) |The SPI Mode for the OLED Display (not typically changed). | -|`OLED_SPI_DIVISOR` |`2` (default) |The SPI Multiplier to use for the OLED Display. | - -## 128x64 & Custom sized OLED Displays - - The default display size for this feature is 128x32, and the defaults are set with that in mind. However, there are a number of additional presets for common sizes that we have added. You can define one of these values to use the presets. If your display doesn't match one of these presets, you can define `OLED_DISPLAY_CUSTOM` to manually specify all of the values. - -|Define |Default |Description | -|----------------------|---------------|---------------------------------------------------------------------------------------------------------------------------------------| -|`OLED_DISPLAY_128X64` |*Not defined* |Changes the display defines for use with 128x64 displays. | -|`OLED_DISPLAY_64X32` |*Not defined* |Changes the display defines for use with 64x32 displays. | -|`OLED_DISPLAY_64X48` |*Not defined* |Changes the display defines for use with 64x48 displays. | -|`OLED_DISPLAY_64X128` |*Not defined* |Changes the display defines for use with 64x128 displays. | -|`OLED_DISPLAY_128X128`|*Not defined* |Changes the display defines for use with 128x128 displays. | -|`OLED_DISPLAY_CUSTOM` |*Not defined* |Changes the display defines for use with custom displays.
Requires user to implement the below defines. | - -::: warning -64x128 and 128x128 displays default to the SH1107 IC type, as these heights are not supported by the other IC types. -::: - -|Define |Default |Description | -| --------------------|---------------|----------------------------------------------------------------------------------------------------------------------------------------| -|`OLED_DISPLAY_WIDTH` |`128` |The width of the OLED display. | -|`OLED_DISPLAY_HEIGHT`|`32` |The height of the OLED display. | -|`OLED_MATRIX_SIZE` |`512` |The local buffer size to allocate.
`(OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH)`. | -|`OLED_BLOCK_TYPE` |`uint16_t` |The unsigned integer type to use for dirty rendering. | -|`OLED_BLOCK_COUNT` |`16` |The number of blocks the display is divided into for dirty rendering.
`(sizeof(OLED_BLOCK_TYPE) * 8)`. | -|`OLED_BLOCK_SIZE` |`32` |The size of each block for dirty rendering
`(OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)`. | -|`OLED_COM_PINS` |`COM_PINS_SEQ` |How the SSD1306 chip maps it's memory to display.
Options are `COM_PINS_SEQ`, `COM_PINS_ALT`, `COM_PINS_SEQ_LR`, & `COM_PINS_ALT_LR`.| -|`OLED_COM_PIN_COUNT` |*Not defined* |Number of COM pins supported by the controller.
If not defined, the value appropriate for the defined `OLED_IC` is used. | -|`OLED_COM_PIN_OFFSET`|`0` |Number of the first COM pin used by the OLED matrix. | -|`OLED_SOURCE_MAP` |`{ 0, ... N }` |Precalculated source array to use for mapping source buffer to target OLED memory in 90 degree rendering. | -|`OLED_TARGET_MAP` |`{ 24, ... N }`|Precalculated target array to use for mapping source buffer to target OLED memory in 90 degree rendering. | - -### 90 Degree Rotation - Technical Mumbo Jumbo - -```c -// OLED Rotation enum values are flags -typedef enum { - OLED_ROTATION_0 = 0, - OLED_ROTATION_90 = 1, - OLED_ROTATION_180 = 2, - OLED_ROTATION_270 = 3, // OLED_ROTATION_90 | OLED_ROTATION_180 -} oled_rotation_t; -``` - -OLED displays driven by SSD1306, SH1106 or SH1107 drivers only natively support in hardware 0 degree and 180 degree rendering. This feature is done in software and not free. Using this feature will increase the time to calculate what data to send over i2c to the OLED. If you are strapped for cycles, this can cause keycodes to not register. In testing however, the rendering time on an ATmega32U4 board only went from 2ms to 5ms and keycodes not registering was only noticed once we hit 15ms. - -90 degree rotation is achieved by using bitwise operations to rotate each 8 block of memory and uses two precalculated arrays to remap buffer memory to OLED memory. The memory map defines are precalculated for remap performance and are calculated based on the display height, width, and block size. For example, in the 128x32 implementation with a `uint8_t` block type, we have a 64 byte block size. This gives us eight 8 byte blocks that need to be rotated and rendered. The OLED renders horizontally two 8 byte blocks before moving down a page, e.g: - -| | | | | | | -|---|---|---|---|---|---| -| 0 | 1 | | | | | -| 2 | 3 | | | | | -| 4 | 5 | | | | | -| 6 | 7 | | | | | - -However the local buffer is stored as if it was Height x Width display instead of Width x Height, e.g: - -| | | | | | | -|---|---|---|---|---|---| -| 3 | 7 | | | | | -| 2 | 6 | | | | | -| 1 | 5 | | | | | -| 0 | 4 | | | | | - -So those precalculated arrays just index the memory offsets in the order in which each one iterates its data. - -Rotation on SH1106 and SH1107 is noticeably less efficient than on SSD1306, because these controllers do not support the “horizontal addressing mode”, which allows transferring the data for the whole rotated block at once; instead, separate address setup commands for every page in the block are required. The screen refresh time for SH1107 is therefore about 45% higher than for a same size screen with SSD1306 when using STM32 MCUs (on AVR the slowdown is about 20%, because the code which actually rotates the bitmap consumes more time). - -## OLED API - -```c -// OLED Rotation enum values are flags -typedef enum { - OLED_ROTATION_0 = 0, - OLED_ROTATION_90 = 1, - OLED_ROTATION_180 = 2, - OLED_ROTATION_270 = 3, // OLED_ROTATION_90 | OLED_ROTATION_180 -} oled_rotation_t; - -// Initialize the oled display, rotating the rendered output based on the define passed in. -// Returns true if the OLED was initialized successfully -bool oled_init(oled_rotation_t rotation); - -// Called at the start of oled_init, weak function overridable by the user -// rotation - the value passed into oled_init -// Return new oled_rotation_t if you want to override default rotation -oled_rotation_t oled_init_kb(oled_rotation_t rotation); -oled_rotation_t oled_init_user(oled_rotation_t rotation); - -// Send commands/data to screen -bool oled_send_cmd(const uint8_t *data, uint16_t size); -bool oled_send_cmd_P(const uint8_t *data, uint16_t size); -bool oled_send_data(const uint8_t *data, uint16_t size); - -// Clears the display buffer, resets cursor position to 0, and sets the buffer to dirty for rendering -void oled_clear(void); - -// Alias to oled_render_dirty to avoid a change in api. -#define oled_render() oled_render_dirty(false) - -// Renders all dirty blocks to the display at one time or a subset depending on the value of -// all. -void oled_render_dirty(bool all); - -// Moves cursor to character position indicated by column and line, wraps if out of bounds -// Max column denoted by 'oled_max_chars()' and max lines by 'oled_max_lines()' functions -void oled_set_cursor(uint8_t col, uint8_t line); - -// Advances the cursor to the next page, writing ' ' if true -// Wraps to the beginning when out of bounds -void oled_advance_page(bool clearPageRemainder); - -// Moves the cursor forward 1 character length -// Advance page if there is not enough room for the next character -// Wraps to the beginning when out of bounds -void oled_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 oled_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 oled_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 oled_write_ln(const char *data, bool invert); - -// Pans the buffer to the right (or left by passing true) by moving contents of the buffer -// Useful for moving the screen in preparation for new drawing -void oled_pan(bool left); - -// Returns a pointer to the requested start index in the buffer plus remaining -// buffer length as struct -oled_buffer_reader_t oled_read_raw(uint16_t start_index); - -// Writes a string to the buffer at current cursor position -void oled_write_raw(const char *data, uint16_t size); - -// Writes a single byte into the buffer at the specified index -void oled_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 oled_write_pixel(uint8_t x, uint8_t y, bool on); - -#if defined(__AVR__) -// Writes a PROGMEM string to the buffer at current cursor position -// Advances the cursor while writing, inverts the pixels if true -// Remapped to call 'void oled_write(const char *data, bool invert);' on ARM -void oled_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 oled_write_ln(const char *data, bool invert);' on ARM -void oled_write_ln_P(const char *data, bool invert); - -// Writes a PROGMEM string to the buffer at current cursor position -void oled_write_raw_P(const char *data, uint16_t size); -#else -# define oled_write_P(data, invert) oled_write(data, invert) -# define oled_write_ln_P(data, invert) oled_write_ln(data, invert) -# define oled_write_raw_P(data, size) oled_write_raw(data, size) -#endif // defined(__AVR__) - -// Can be used to manually turn on the screen if it is off -// Returns true if the screen was on or turns on -bool oled_on(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 oled_off(void); - -// Returns true if the oled is currently on, false if it is -// not -bool is_oled_on(void); - -// Sets the brightness level of the display -uint8_t oled_set_brightness(uint8_t level); - -// Gets the current brightness level of the display -uint8_t oled_get_brightness(void); - -// Basically it's oled_render, but with timeout management and oled_task_user calling! -void oled_task(void); - -// Called at the start of oled_task, weak function overridable by the user -bool oled_task_kb(void); -bool oled_task_user(void); - -// Set the specific 8 lines rows of the screen to scroll. -// 0 is the default for start, and 7 for end, which is the entire -// height of the screen. For 128x32 screens, rows 4-7 are not used. -void oled_scroll_set_area(uint8_t start_line, uint8_t end_line); - -// Sets scroll speed, 0-7, fastest to slowest. Default is three. -// Does not take effect until scrolling is either started or restarted -// the ssd1306 supports 8 speeds with the delay -// listed below between each frame of the scrolling effect -// 0=2, 1=3, 2=4, 3=5, 4=25, 5=64, 6=128, 7=256 -void oled_scroll_set_speed(uint8_t speed); - -// Begin scrolling the entire display right -// Returns true if the screen was scrolling or starts scrolling -// NOTE: display contents cannot be changed while scrolling -bool oled_scroll_right(void); - -// Begin scrolling the entire display left -// Returns true if the screen was scrolling or starts scrolling -// NOTE: display contents cannot be changed while scrolling -bool oled_scroll_left(void); - -// Turns off display scrolling -// Returns true if the screen was not scrolling or stops scrolling -bool oled_scroll_off(void); - -// Returns true if the oled is currently scrolling, false if it is -// not -bool is_oled_scrolling(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); - -// Returns the maximum number of lines that will fit on the OLED -uint8_t oled_max_lines(void); -``` - -::: warning -Scrolling is unsupported on the SH1106 and SH1107. -::: - -::: warning -Scrolling does not work properly on the SSD1306 if the display width is smaller than 128. -::: - -## SSD1306.h Driver Conversion Guide - -|Old API |Recommended New API | -|-------------------------|---------------------------------| -|`struct CharacterMatrix` |*removed - delete all references*| -|`iota_gfx_init` |`oled_init` | -|`iota_gfx_on` |`oled_on` | -|`iota_gfx_off` |`oled_off` | -|`iota_gfx_flush` |`oled_render` | -|`iota_gfx_write_char` |`oled_write_char` | -|`iota_gfx_write` |`oled_write` | -|`iota_gfx_write_P` |`oled_write_P` | -|`iota_gfx_clear_screen` |`oled_clear` | -|`matrix_clear` |*removed - delete all references*| -|`matrix_write_char_inner`|`oled_write_char` | -|`matrix_write_char` |`oled_write_char` | -|`matrix_write` |`oled_write` | -|`matrix_write_ln` |`oled_write_ln` | -|`matrix_write_P` |`oled_write_P` | -|`matrix_write_ln_P` |`oled_write_ln_P` | -|`matrix_render` |`oled_render` | -|`iota_gfx_task` |`oled_task` | -|`iota_gfx_task_user` |`oled_task_user` | diff --git a/docs/feature_os_detection.md b/docs/feature_os_detection.md deleted file mode 100644 index d0556d2549..0000000000 --- a/docs/feature_os_detection.md +++ /dev/null @@ -1,132 +0,0 @@ -# OS Detection - -This feature makes a best guess at the host OS based on OS specific behavior during USB setup. It may not always get the correct OS, and shouldn't be relied on as for critical functionality. - -Using it you can have OS specific key mappings or combos which work differently on different devices. - -It is available for keyboards which use ChibiOS, LUFA and V-USB. - -## Usage - -In your `rules.mk` add: - -```make -OS_DETECTION_ENABLE = yes -``` - -It will automatically include the required headers file. -It declares `os_variant_t detected_host_os(void);` which you can call to get detected OS. - -It returns one of the following values: - -```c -enum { - OS_UNSURE, - OS_LINUX, - OS_WINDOWS, - OS_MACOS, - OS_IOS, -} os_variant_t; -``` - -::: tip -Note that it takes some time after firmware is booted to detect the OS. -::: -This time is quite short, probably hundreds of milliseconds, but this data may be not ready in keyboard and layout setup functions which run very early during firmware startup. - -## Callbacks {#callbacks} - -If you want to perform custom actions when the OS is detected, then you can use the `process_detected_host_os_kb` function on the keyboard level source file, or `process_detected_host_os_user` function in the user `keymap.c`. - -```c -bool process_detected_host_os_kb(os_variant_t detected_os) { - if (!process_detected_host_os_user(detected_os)) { - return false; - } - switch (detected_os) { - case OS_MACOS: - case OS_IOS: - rgb_matrix_set_color_all(RGB_WHITE); - break; - case OS_WINDOWS: - rgb_matrix_set_color_all(RGB_BLUE); - break; - case OS_LINUX: - rgb_matrix_set_color_all(RGB_ORANGE); - break; - case OS_UNSURE: - rgb_matrix_set_color_all(RGB_RED); - break; - } - - return true; -} -``` - -## OS detection stability - -The OS detection is currently handled while the USB device descriptor is being assembled. -The process is done in steps, generating a number of intermediate results until it stabilizes. -We therefore resort to debouncing the result until it has been stable for a given amount of milliseconds. -This amount can be configured, in case your board is not stable within the default debouncing time of 200ms. - -## KVM and USB switches - -Some KVM and USB switches may not trigger the USB controller on the keyboard to fully reset upon switching machines. -If your keyboard does not redetect the OS in this situation, you can force the keyboard to reset when the USB initialization event is detected, forcing the USB controller to be reconfigured. - -## Configuration Options - -* `#define OS_DETECTION_DEBOUNCE 200` - * defined the debounce time for OS detection, in milliseconds -* `#define OS_DETECTION_KEYBOARD_RESET` - * enables the keyboard reset upon a USB device reinitilization, such as switching devices on some KVMs - -## Debug - -If OS is guessed incorrectly, you may want to collect data about USB setup packets to refine the detection logic. - -To do so in your `config.h` add: - -```c -#define OS_DETECTION_DEBUG_ENABLE -``` - -And in your `rules.mk` add: - -```make -CONSOLE_ENABLE = yes -``` - -And also include `"os_detection.h"` in your `keymap.c`. - -Then you can define custom keycodes to store data about USB setup packets in EEPROM (persistent memory) and to print it later on host where you can run `qmk console`: - -```c -enum custom_keycodes { - STORE_SETUPS = SAFE_RANGE, - PRINT_SETUPS, -}; - -bool process_record_user(uint16_t keycode, keyrecord_t *record) { - switch (keycode) { - case STORE_SETUPS: - if (record->event.pressed) { - store_setups_in_eeprom(); - } - return false; - case PRINT_SETUPS: - if (record->event.pressed) { - print_stored_setups(); - } - return false; - } -} -``` - -Then please open an issue on Github with this information and tell what OS was not detected correctly and if you have any intermediate devices between keyboard and your computer. - - -## Credits - -Original idea is coming from [FingerprintUSBHost](https://github.com/keyboardio/FingerprintUSBHost) project. diff --git a/docs/feature_pointing_device.md b/docs/feature_pointing_device.md deleted file mode 100644 index 933202a009..0000000000 --- a/docs/feature_pointing_device.md +++ /dev/null @@ -1,928 +0,0 @@ -# Pointing Device {#pointing-device} - -Pointing Device is a generic name for a feature intended to be generic: moving the system pointer around. There are certainly other options for it - like mousekeys - but this aims to be easily modifiable and hardware driven. You can implement custom keys to control functionality, or you can gather information from other peripherals and insert it directly here - let QMK handle the processing for you. - -To enable Pointing Device, add the following line in your rules.mk and specify one of the driver options below. - -```make -POINTING_DEVICE_ENABLE = yes -``` - -## Sensor Drivers - -There are a number of sensors that are supported by default. Note that only one sensor can be enabled by `POINTING_DEVICE_DRIVER` at a time. If you need to enable more than one sensor, then you need to implement it manually, using the `custom` driver. - -### ADNS 5050 Sensor - -To use the ADNS 5050 sensor, add this to your `rules.mk` - -```make -POINTING_DEVICE_DRIVER = adns5050 -``` - -The ADNS 5050 sensor uses a serial type protocol for communication, and requires an additional light source. - -| Setting (`config.h`) | Description | Default | -| -------------------- | ------------------------------------------------------------------ | -------------------------- | -| `ADNS5050_SCLK_PIN` | (Required) The pin connected to the clock pin of the sensor. | `POINTING_DEVICE_SCLK_PIN` | -| `ADNS5050_SDIO_PIN` | (Required) The pin connected to the data pin of the sensor. | `POINTING_DEVICE_SDIO_PIN` | -| `ADNS5050_CS_PIN` | (Required) The pin connected to the Chip Select pin of the sensor. | `POINTING_DEVICE_CS_PIN` | - - - -The CPI range is 125-1375, in increments of 125. Defaults to 500 CPI. - -### ADNS 9800 Sensor - -To use the ADNS 9800 sensor, add this to your `rules.mk` - -```make -POINTING_DEVICE_DRIVER = adns9800 -``` - -The ADNS 9800 is an SPI driven optical sensor, that uses laser output for surface tracking. - -| Setting (`config.h`) | Description | Default | -| ----------------------- | ---------------------------------------------------------------------- | ------------------------ | -| `ADNS9800_CLOCK_SPEED` | (Optional) Sets the clock speed that the sensor runs at. | `2000000` | -| `ADNS9800_SPI_LSBFIRST` | (Optional) Sets the Least/Most Significant Byte First setting for SPI. | `false` | -| `ADNS9800_SPI_MODE` | (Optional) Sets the SPI Mode for the sensor. | `3` | -| `ADNS9800_SPI_DIVISOR` | (Optional) Sets the SPI Divisor used for SPI communication. | _varies_ | -| `ADNS9800_CS_PIN` | (Required) Sets the Chip Select pin connected to the sensor. | `POINTING_DEVICE_CS_PIN` | - - -The CPI range is 800-8200, in increments of 200. Defaults to 1800 CPI. - -### Analog Joystick - -To use an analog joystick to control the pointer, add this to your `rules.mk` - -```make -POINTING_DEVICE_DRIVER = analog_joystick -``` - -The Analog Joystick is an analog (ADC) driven sensor. There are a variety of joysticks that you can use for this. - -| Setting (`config.h`) | Description | Default | -| --------------------------------- | -------------------------------------------------------------------------- | ------------- | -| `ANALOG_JOYSTICK_X_AXIS_PIN` | (Required) The pin used for the vertical/X axis. | _not defined_ | -| `ANALOG_JOYSTICK_Y_AXIS_PIN` | (Required) The pin used for the horizontal/Y axis. | _not defined_ | -| `ANALOG_JOYSTICK_AXIS_MIN` | (Optional) Sets the lower range to be considered movement. | `0` | -| `ANALOG_JOYSTICK_AXIS_MAX` | (Optional) Sets the upper range to be considered movement. | `1023` | -| `ANALOG_JOYSTICK_AUTO_AXIS` | (Optional) Sets ranges to be considered movement automatically. | _not defined_ | -| `ANALOG_JOYSTICK_SPEED_REGULATOR` | (Optional) The divisor used to slow down movement. (lower makes it faster) | `20` | -| `ANALOG_JOYSTICK_READ_INTERVAL` | (Optional) The interval in milliseconds between reads. | `10` | -| `ANALOG_JOYSTICK_SPEED_MAX` | (Optional) The maximum value used for motion. | `2` | -| `ANALOG_JOYSTICK_CLICK_PIN` | (Optional) The pin wired up to the press switch of the analog stick. | _not defined_ | -| `ANALOG_JOYSTICK_WEIGHTS` | (Optional) Use custom weights for lever positions. | _not defined_ | -| `ANALOG_JOYSTICK_CUTOFF` | (Optional) Cut off movement when joystick returns to start position. | _not defined_ | - -If `ANALOG_JOYSTICK_AUTO_AXIS` is used, then `ANALOG_JOYSTICK_AXIS_MIN` and `ANALOG_JOYSTICK_AXIS_MAX` are ignored. - -By default analog joystick implementation uses `x^2` weighting for lever positions. `ANALOG_JOYSTICK_WEIGHTS` allows to experiment with different configurations that might feel better. - -E.g. This is weights for `((x-0.4)^3+0.064)/0.282`: - -```c -#define ANALOG_JOYSTICK_WEIGHTS {0,2,4,5,7,8,9,10,12,13,14,15,15,16,17,18,18,19,19,20,20,21,21,21,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,25,25,25,26,26,26,27,28,28,29,29,30,31,32,33,34,35,36,37,38,40,41,43,44,46,48,49,51,53,56,58,60,62,65,68,70,73,76,79,82,85,89,92,96,100} -``` - -You can use following JS code to generate weights for different formulas: - -```js -JSON.stringify(Array.from(Array(101).keys()).map(x => Math.ceil((((x/100-0.4)**3+0.064)/0.282*100)))) -``` - -### Azoteq IQS5XX Trackpad - -To use a Azoteq IQS5XX trackpad, add this to your `rules.mk`: - -```make -POINTING_DEVICE_DRIVER = azoteq_iqs5xx -``` - -This supports the IQS525, IQS550 and IQS572 controllers, with the latter two being used in the TPS43 and TPS65 trackpads. - -#### Device settings - -Specific device profiles are provided which set the required values for dimensions and resolution. - -| Setting | Description | -| -------------------------------- | ---------------------------------------------------------- | -| `AZOTEQ_IQS5XX_TPS43` | (Pick One) Sets resolution/mm to TPS43 specifications. | -| `AZOTEQ_IQS5XX_TPS65` | (Pick One) Sets resolution/mm to TPS65 specifications. | - -::: tip -If using one of the above defines you can skip to gesture settings. -::: - -| Setting | Description | Default | -| -------------------------------- | ---------------------------------------------------------- | ------------- | -| `AZOTEQ_IQS5XX_WIDTH_MM` | (Required) Width of the trackpad sensor in millimeters. | _not defined_ | -| `AZOTEQ_IQS5XX_HEIGHT_MM` | (Required) Height of the trackpad sensor in millimeters. | _not defined_ | -| `AZOTEQ_IQS5XX_RESOLUTION_X` | (Optional) Specify X resolution for CPI calculation. | _not defined_ | -| `AZOTEQ_IQS5XX_RESOLUTION_Y` | (Optional) Specify Y resolution for CPI calculation. | _not defined_ | - -**`AZOTEQ_IQS5XX_RESOLUTION_X/Y`** fall back resolutions are provided within the driver based on controller model. - -| I2C Setting | Description | Default | -| ------------------------- | ------------------------------------------------------------------------------- | ------- | -| `AZOTEQ_IQS5XX_ADDRESS` | (Optional) Sets the I2C Address for the Azoteq trackpad | `0xE8` | -| `AZOTEQ_IQS5XX_TIMEOUT_MS`| (Optional) The timeout for i2c communication with in milliseconds. | `10` | - -#### Gesture settings - -| Setting | Description | Default | -| ----------------------------------------- | ------------------------------------------------------------------------------------ | ----------- | -| `AZOTEQ_IQS5XX_TAP_ENABLE` | (Optional) Enable single finger tap. (Left click) | `true` | -| `AZOTEQ_IQS5XX_TWO_FINGER_TAP_ENABLE` | (Optional) Enable two finger tap. (Right click) | `true` | -| `AZOTEQ_IQS5XX_PRESS_AND_HOLD_ENABLE` | (Optional) Emulates holding left click to select text. | `false` | -| `AZOTEQ_IQS5XX_SWIPE_X_ENABLE` | (Optional) Enable swipe gestures X+ (Mouse Button 5) / X- (Mouse Button 4) | `false` | -| `AZOTEQ_IQS5XX_SWIPE_Y_ENABLE` | (Optional) Enable swipe gestures Y+ (Mouse Button 3) / Y- (Mouse Button 6) | `false` | -| `AZOTEQ_IQS5XX_ZOOM_ENABLE` | (Optional) Enable zoom gestures Zoom Out (Mouse Button 7) / Zoom In (Mouse Button 8) | `false` | -| `AZOTEQ_IQS5XX_SCROLL_ENABLE` | (Optional) Enable scrolling using two fingers. | `true` | -| `AZOTEQ_IQS5XX_TAP_TIME` | (Optional) Maximum time in ms for tap to be registered. | `150` | -| `AZOTEQ_IQS5XX_TAP_DISTANCE` | (Optional) Maximum deviation in pixels before single tap is no longer valid. | `25` | -| `AZOTEQ_IQS5XX_HOLD_TIME` | (Optional) Minimum time in ms for press and hold. | `300` | -| `AZOTEQ_IQS5XX_SWIPE_INITIAL_TIME` | (Optional) Maximum time to travel initial distance before swipe is registered. | `150` | -| `AZOTEQ_IQS5XX_SWIPE_INITIAL_DISTANCE` | (Optional) Minimum travel in pixels before swipe is registered. | `300` | -| `AZOTEQ_IQS5XX_SWIPE_CONSECUTIVE_TIME` | (Optional) Maximum time to travel consecutive distance before swipe is registered. | `0` | -| `AZOTEQ_IQS5XX_SWIPE_CONSECUTIVE_DISTANCE`| (Optional) Minimum travel in pixels before a consecutive swipe is registered. | `2000` | -| `AZOTEQ_IQS5XX_SCROLL_INITIAL_DISTANCE` | (Optional) Minimum travel in pixels before scroll is registered. | `50` | -| `AZOTEQ_IQS5XX_ZOOM_INITIAL_DISTANCE` | (Optional) Minimum travel in pixels before zoom is registered. | `50` | -| `AZOTEQ_IQS5XX_ZOOM_CONSECUTIVE_DISTANCE` | (Optional) Maximum time to travel zoom distance before zoom is registered. | `25` | - -#### Rotation settings - -| Setting | Description | Default | -| ---------------------------- | ---------------------------------------------------------- | ------------- | -| `AZOTEQ_IQS5XX_ROTATION_90` | (Optional) Configures hardware for 90 degree rotation. | _not defined_ | -| `AZOTEQ_IQS5XX_ROTATION_180` | (Optional) Configures hardware for 180 degree rotation. | _not defined_ | -| `AZOTEQ_IQS5XX_ROTATION_270` | (Optional) Configures hardware for 270 degree rotation. | _not defined_ | - -### Cirque Trackpad - -To use the Cirque Trackpad sensor, add this to your `rules.mk`: - -```make -POINTING_DEVICE_DRIVER = cirque_pinnacle_i2c -``` - -or - -```make -POINTING_DEVICE_DRIVER = cirque_pinnacle_spi -``` - - -This supports the Cirque Pinnacle 1CA027 Touch Controller, which is used in the TM040040, TM035035 and the TM023023 trackpads. These are I2C or SPI compatible, and both configurations are supported. - -#### Common settings - -| Setting | Description | Default | -| ------------------------------------ | ---------------------------------------------------------- | ------------------------------------------- | -| `CIRQUE_PINNACLE_DIAMETER_MM` | (Optional) Diameter of the trackpad sensor in millimeters. | `40` | -| `CIRQUE_PINNACLE_ATTENUATION` | (Optional) Sets the attenuation of the sensor data. | `EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_4X` | -| `CIRQUE_PINNACLE_CURVED_OVERLAY` | (Optional) Applies settings tuned for curved overlay. | _not defined_ | -| `CIRQUE_PINNACLE_POSITION_MODE` | (Optional) Mode of operation. | _not defined_ | -| `CIRQUE_PINNACLE_SKIP_SENSOR_CHECK` | (Optional) Skips sensor presence check | _not defined_ | - -**`CIRQUE_PINNACLE_ATTENUATION`** is a measure of how much data is suppressed in regards to sensitivity. The higher the attenuation, the less sensitive the touchpad will be. - -Default attenuation is set to 4X, although if you are using a thicker overlay (such as the curved overlay) you will want a lower attenuation such as 2X. The possible values are: -* `EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_4X`: Least sensitive -* `EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_3X` -* `EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_2X` -* `EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_1X`: Most sensitive - -**`CIRQUE_PINNACLE_POSITION_MODE`** can be `CIRQUE_PINNACLE_ABSOLUTE_MODE` or `CIRQUE_PINNACLE_RELATIVE_MODE`. Modes differ in supported features/gestures. - -* `CIRQUE_PINNACLE_ABSOLUTE_MODE`: Reports absolute x, y, z (touch pressure) coordinates and up to 5 hw buttons connected to the trackpad -* `CIRQUE_PINNACLE_RELATIVE_MODE`: Reports x/y deltas, scroll and up to 3 buttons (2 of them can be from taps, see gestures) connected to trackpad. Supports taps on secondary side of split. Saves about 2k of flash compared to absolute mode with all features. - -| I2C Setting | Description | Default | -| ------------------------- | ------------------------------------------------------------------------------- | ------- | -| `CIRQUE_PINNACLE_ADDR` | (Required) Sets the I2C Address for the Cirque Trackpad | `0x2A` | -| `CIRQUE_PINNACLE_TIMEOUT` | (Optional) The timeout for i2c communication with the trackpad in milliseconds. | `20` | - -| SPI Setting | Description | Default | -| ------------------------------ | ---------------------------------------------------------------------- | ------------------------ | -| `CIRQUE_PINNACLE_CLOCK_SPEED` | (Optional) Sets the clock speed that the sensor runs at. | `1000000` | -| `CIRQUE_PINNACLE_SPI_LSBFIRST` | (Optional) Sets the Least/Most Significant Byte First setting for SPI. | `false` | -| `CIRQUE_PINNACLE_SPI_MODE` | (Optional) Sets the SPI Mode for the sensor. | `1` | -| `CIRQUE_PINNACLE_SPI_DIVISOR` | (Optional) Sets the SPI Divisor used for SPI communication. | _varies_ | -| `CIRQUE_PINNACLE_SPI_CS_PIN` | (Required) Sets the Chip Select pin connected to the sensor. | `POINTING_DEVICE_CS_PIN` | - -Default Scaling is 1024. Actual CPI depends on trackpad diameter. - -Also see the `POINTING_DEVICE_TASK_THROTTLE_MS`, which defaults to 10ms when using Cirque Pinnacle, which matches the internal update rate of the position registers (in standard configuration). Advanced configuration for pen/stylus usage might require lower values. - -#### Absolute mode settings - -| Setting | Description | Default | -|-----------------------------------------|-------------------------------------------------------------------------|-------------| -| `CIRQUE_PINNACLE_X_LOWER` | (Optional) The minimum reachable X value on the sensor. | `127` | -| `CIRQUE_PINNACLE_X_UPPER` | (Optional) The maximum reachable X value on the sensor. | `1919` | -| `CIRQUE_PINNACLE_Y_LOWER` | (Optional) The minimum reachable Y value on the sensor. | `63` | -| `CIRQUE_PINNACLE_Y_UPPER` | (Optional) The maximum reachable Y value on the sensor. | `1471` | -| `CIRQUE_PINNACLE_REACHABLE_CALIBRATION` | (Optional) Enable console messages to aide in calibrating above values. | not defined | - -#### Absolute mode gestures - -| Gesture Setting | Description | Default | -| ---------------------------------------------- | ------------------------------------------------------------------------------ | -------------------- | -| `CIRQUE_PINNACLE_TAP_ENABLE` | (Optional) Enable tap to click. This currently only works on the master side. | _not defined_ | -| `CIRQUE_PINNACLE_TAPPING_TERM` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` | -| `CIRQUE_PINNACLE_TOUCH_DEBOUNCE` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` | - -`POINTING_DEVICE_GESTURES_SCROLL_ENABLE` in this mode enables circular scroll. Touch originating in outer ring can trigger scroll by moving along the perimeter. Near side triggers vertical scroll and far side triggers horizontal scroll. - -Additionally, `POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE` is supported in this mode. - -#### Relative mode gestures - -| Gesture Setting (`config.h`) | Description | Default | -| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | -| `CIRQUE_PINNACLE_TAP_ENABLE` | (Optional) Enable tap to "left click". Works on both sides of a split keyboard. | _not defined_ | -| `CIRQUE_PINNACLE_SECONDARY_TAP_ENABLE` | (Optional) Tap in upper right corner (half of the finger needs to be outside of the trackpad) of the trackpad will result in "right click". `CIRQUE_PINNACLE_TAP_ENABLE` must be enabled. | _not defined_ | - -Tapping term and debounce are not configurable in this mode since it's handled by trackpad internally. - -`POINTING_DEVICE_GESTURES_SCROLL_ENABLE` in this mode enables side scroll. Touch originating on the right side can trigger vertical scroll (IntelliSense trackpad style). - -### PAW 3204 Sensor - -To use the paw 3204 sensor, add this to your `rules.mk` - -```make -POINTING_DEVICE_DRIVER = paw3204 -``` - -The paw 3204 sensor uses a serial type protocol for communication, and requires an additional light source. - -| Setting (`config.h`) | Description | Default | -| -------------------- |--------------------------------------------------------------- | -------------------------- | -| `PAW3204_SCLK_PIN` | (Required) The pin connected to the clock pin of the sensor. | `POINTING_DEVICE_SCLK_PIN` | -| `PAW3204_SDIO_PIN` | (Required) The pin connected to the data pin of the sensor. | `POINTING_DEVICE_SDIO_PIN` | - -The CPI range is 400-1600, with supported values of (400, 500, 600, 800, 1000, 1200 and 1600). Defaults to 1000 CPI. - -### Pimoroni Trackball - -To use the Pimoroni Trackball module, add this to your `rules.mk`: - -```make -POINTING_DEVICE_DRIVER = pimoroni_trackball -``` - -The Pimoroni Trackball module is a I2C based breakout board with an RGB enable trackball. - -| Setting (`config.h`) | Description | Default | -| ------------------------------------ | ---------------------------------------------------------------------------------- | ------- | -| `PIMORONI_TRACKBALL_ADDRESS` | (Required) Sets the I2C Address for the Pimoroni Trackball. | `0x0A` | -| `PIMORONI_TRACKBALL_TIMEOUT` | (Optional) The timeout for i2c communication with the trackball in milliseconds. | `100` | -| `PIMORONI_TRACKBALL_SCALE` | (Optional) The multiplier used to generate reports from the sensor. | `5` | -| `PIMORONI_TRACKBALL_DEBOUNCE_CYCLES` | (Optional) The number of scan cycles used for debouncing on the ball press. | `20` | -| `PIMORONI_TRACKBALL_ERROR_COUNT` | (Optional) Specifies the number of read/write errors until the sensor is disabled. | `10` | - -### PMW3320 Sensor - -To use the PMW3320 sensor, add this to your `rules.mk` - -```make -POINTING_DEVICE_DRIVER = pmw3320 -``` - -The PMW3320 sensor uses a serial type protocol for communication, and requires an additional light source (it could work without one, but expect it to be out of service early). - -| Setting | Description | Default | -| ------------------- | ------------------------------------------------------------------- | -------------------------- | -| `PMW3320_SCLK_PIN` | (Required) The pin connected to the clock pin of the sensor. | `POINTING_DEVICE_SCLK_PIN` | -| `PMW3320_SDIO_PIN` | (Required) The pin connected to the data pin of the sensor. | `POINTING_DEVICE_SDIO_PIN` | -| `PMW3320_CS_PIN` | (Required) The pin connected to the cable select pin of the sensor. | `POINTING_DEVICE_CS_PIN` | - -The CPI range is 500-3500, in increments of 250. Defaults to 1000 CPI. - -### PMW 3360 and PMW 3389 Sensor - -This drivers supports both the PMW 3360 and PMW 3389 sensor as well as multiple sensors of the same type _per_ controller, so 2 can be attached at the same side for split keyboards (or unsplit keyboards). - -To use the **PMW 3360** sensor, add this to your `rules.mk` - -```make -POINTING_DEVICE_DRIVER = pmw3360 -``` - -The CPI range is 100-12000, in increments of 100. Defaults to 1600 CPI. - -To use the **PMW 3389** sensor, add this to your `rules.mk` - -```make -POINTING_DEVICE_DRIVER = pmw3389 -``` - -The CPI range is 50-16000, in increments of 50. Defaults to 2000 CPI. - -Both PMW 3360 and PMW 3389 are SPI driven optical sensors, that use a built in IR LED for surface tracking. -If you have different CS wiring on each half you can use `PMW33XX_CS_PIN_RIGHT` or `PMW33XX_CS_PINS_RIGHT` in combination with `PMW33XX_CS_PIN` or `PMW33XX_CS_PINS` to configure both sides independently. If `_RIGHT` values aren't provided, they default to be the same as the left ones. - -| Setting (`config.h`) | Description | Default | -| ---------------------------- | ------------------------------------------------------------------------------------------- | ------------------------ | -| `PMW33XX_CS_PIN` | (Required) Sets the Chip Select pin connected to the sensor. | `POINTING_DEVICE_CS_PIN` | -| `PMW33XX_CS_PINS` | (Alternative) Sets the Chip Select pins connected to multiple sensors. | `{PMW33XX_CS_PIN}` | -| `PMW33XX_CS_PIN_RIGHT` | (Optional) Sets the Chip Select pin connected to the sensor on the right half. | `PMW33XX_CS_PIN` | -| `PMW33XX_CS_PINS_RIGHT` | (Optional) Sets the Chip Select pins connected to multiple sensors on the right half. | `{PMW33XX_CS_PIN_RIGHT}` | -| `PMW33XX_CPI` | (Optional) Sets counts per inch sensitivity of the sensor. | _varies_ | -| `PMW33XX_CLOCK_SPEED` | (Optional) Sets the clock speed that the sensor runs at. | `2000000` | -| `PMW33XX_SPI_DIVISOR` | (Optional) Sets the SPI Divisor used for SPI communication. | _varies_ | -| `PMW33XX_LIFTOFF_DISTANCE` | (Optional) Sets the lift off distance at run time | `0x02` | -| `ROTATIONAL_TRANSFORM_ANGLE` | (Optional) Allows for the sensor data to be rotated +/- 127 degrees directly in the sensor. | `0` | - -To use multiple sensors, instead of setting `PMW33XX_CS_PIN` you need to set `PMW33XX_CS_PINS` and also handle and merge the read from this sensor in user code. -Note that different (per sensor) values of CPI, speed liftoff, rotational angle or flipping of X/Y is not currently supported. - -```c -// in config.h: -#define PMW33XX_CS_PINS { B5, B6 } -// in keyboard.c: -#ifdef POINTING_DEVICE_ENABLE -void pointing_device_init_kb(void) { - pmw33xx_init(1); // index 1 is the second device. - pmw33xx_set_cpi(0, 800); // applies to first sensor - pmw33xx_set_cpi(1, 800); // applies to second sensor - pointing_device_init_user(); -} - -// Contains report from sensor #0 already, need to merge in from sensor #1 -report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { - pmw33xx_report_t report = pmw33xx_read_burst(1); - if (!report.motion.b.is_lifted && report.motion.b.is_motion) { -// From quantum/pointing_device_drivers.c -#define constrain_hid(amt) ((amt) < -127 ? -127 : ((amt) > 127 ? 127 : (amt))) - mouse_report.x = constrain_hid(mouse_report.x + report.delta_x); - mouse_report.y = constrain_hid(mouse_report.y + report.delta_y); - } - return pointing_device_task_user(mouse_report); -} -#endif - -``` - -### Custom Driver - -If you have a sensor type that isn't supported above, a custom option is available by adding the following to your `rules.mk` - -```make -POINTING_DEVICE_DRIVER = custom -``` - -Using the custom driver will require implementing the following functions: - -```c -void pointing_device_driver_init(void) {} -report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) { return mouse_report; } -uint16_t pointing_device_driver_get_cpi(void) { return 0; } -void pointing_device_driver_set_cpi(uint16_t cpi) {} -``` - -::: warning -Ideally, new sensor hardware should be added to `drivers/sensors/` and `quantum/pointing_device_drivers.c`, but there may be cases where it's very specific to the hardware. So these functions are provided, just in case. -::: - -## Common Configuration - -| Setting | Description | Default | -| ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------- | -| `MOUSE_EXTENDED_REPORT` | (Optional) Enables support for extended mouse reports. (-32767 to 32767, instead of just -127 to 127). | _not defined_ | -| `POINTING_DEVICE_ROTATION_90` | (Optional) Rotates the X and Y data by 90 degrees. | _not defined_ | -| `POINTING_DEVICE_ROTATION_180` | (Optional) Rotates the X and Y data by 180 degrees. | _not defined_ | -| `POINTING_DEVICE_ROTATION_270` | (Optional) Rotates the X and Y data by 270 degrees. | _not defined_ | -| `POINTING_DEVICE_INVERT_X` | (Optional) Inverts the X axis report. | _not defined_ | -| `POINTING_DEVICE_INVERT_Y` | (Optional) Inverts the Y axis report. | _not defined_ | -| `POINTING_DEVICE_MOTION_PIN` | (Optional) If supported, will only read from sensor if pin is active. | _not defined_ | -| `POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW` | (Optional) If defined then the motion pin is active-low. | _varies_ | -| `POINTING_DEVICE_TASK_THROTTLE_MS` | (Optional) Limits the frequency that the sensor is polled for motion. | _not defined_ | -| `POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE` | (Optional) Enable inertial cursor. Cursor continues moving after a flick gesture and slows down by kinetic friction. | _not defined_ | -| `POINTING_DEVICE_GESTURES_SCROLL_ENABLE` | (Optional) Enable scroll gesture. The gesture that activates the scroll is device dependent. | _not defined_ | -| `POINTING_DEVICE_CS_PIN` | (Optional) Provides a default CS pin, useful for supporting multiple sensor configs. | _not defined_ | -| `POINTING_DEVICE_SDIO_PIN` | (Optional) Provides a default SDIO pin, useful for supporting multiple sensor configs. | _not defined_ | -| `POINTING_DEVICE_SCLK_PIN` | (Optional) Provides a default SCLK pin, useful for supporting multiple sensor configs. | _not defined_ | - -::: warning -When using `SPLIT_POINTING_ENABLE` the `POINTING_DEVICE_MOTION_PIN` functionality is not supported and `POINTING_DEVICE_TASK_THROTTLE_MS` will default to `1`. Increasing this value will increase transport performance at the cost of possible mouse responsiveness. -::: - -The `POINTING_DEVICE_CS_PIN`, `POINTING_DEVICE_SDIO_PIN`, and `POINTING_DEVICE_SCLK_PIN` provide a convenient way to define a single pin that can be used for an interchangeable sensor config. This allows you to have a single config, without defining each device. Each sensor allows for this to be overridden with their own defines. - -::: warning -Any pointing device with a lift/contact status can integrate inertial cursor feature into its driver, controlled by `POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE`. e.g. PMW3360 can use Lift_Stat from Motion register. Note that `POINTING_DEVICE_MOTION_PIN` cannot be used with this feature; continuous polling of `get_report()` is needed to generate glide reports. -::: - -## Split Keyboard Configuration - -The following configuration options are only available when using `SPLIT_POINTING_ENABLE` see [data sync options](feature_split_keyboard#data-sync-options). The rotation and invert `*_RIGHT` options are only used with `POINTING_DEVICE_COMBINED`. If using `POINTING_DEVICE_LEFT` or `POINTING_DEVICE_RIGHT` use the common configuration above to configure your pointing device. - -| Setting | Description | Default | -| ------------------------------------ | ----------------------------------------------------------------------------------------------------- | ------------- | -| `POINTING_DEVICE_LEFT` | Pointing device on the left side (Required - pick one only) | _not defined_ | -| `POINTING_DEVICE_RIGHT` | Pointing device on the right side (Required - pick one only) | _not defined_ | -| `POINTING_DEVICE_COMBINED` | Pointing device on both sides (Required - pick one only) | _not defined_ | -| `POINTING_DEVICE_ROTATION_90_RIGHT` | (Optional) Rotates the X and Y data by 90 degrees. | _not defined_ | -| `POINTING_DEVICE_ROTATION_180_RIGHT` | (Optional) Rotates the X and Y data by 180 degrees. | _not defined_ | -| `POINTING_DEVICE_ROTATION_270_RIGHT` | (Optional) Rotates the X and Y data by 270 degrees. | _not defined_ | -| `POINTING_DEVICE_INVERT_X_RIGHT` | (Optional) Inverts the X axis report. | _not defined_ | -| `POINTING_DEVICE_INVERT_Y_RIGHT` | (Optional) Inverts the Y axis report. | _not defined_ | - -::: warning -If there is a `_RIGHT` configuration option or callback, the [common configuration](feature_pointing_device#common-configuration) option will work for the left. For correct left/right detection you should setup a [handedness option](feature_split_keyboard#setting-handedness), `EE_HANDS` is usually a good option for an existing board that doesn't do handedness by hardware. -::: - - -## Callbacks and Functions - -| Function | Description | -| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | -| `pointing_device_init_kb(void)` | Callback to allow for keyboard level initialization. Useful for additional hardware sensors. | -| `pointing_device_init_user(void)` | Callback to allow for user level initialization. Useful for additional hardware sensors. | -| `pointing_device_task_kb(mouse_report)` | Callback that sends sensor data, so keyboard code can intercept and modify the data. Returns a mouse report. | -| `pointing_device_task_user(mouse_report)` | Callback that sends sensor data, so user code can intercept and modify the data. Returns a mouse report. | -| `pointing_device_handle_buttons(buttons, pressed, button)` | Callback to handle hardware button presses. Returns a `uint8_t`. | -| `pointing_device_get_cpi(void)` | Gets the current CPI/DPI setting from the sensor, if supported. | -| `pointing_device_set_cpi(uint16_t)` | Sets the CPI/DPI, if supported. | -| `pointing_device_get_report(void)` | Returns the current mouse report (as a `report_mouse_t` data structure). | -| `pointing_device_set_report(mouse_report)` | Sets the mouse report to the assigned `report_mouse_t` data structured passed to the function. | -| `pointing_device_send(void)` | Sends the current mouse report to the host system. Function can be replaced. | -| `has_mouse_report_changed(new_report, old_report)` | Compares the old and new `report_mouse_t` data and returns true only if it has changed. | -| `pointing_device_adjust_by_defines(mouse_report)` | Applies rotations and invert configurations to a raw mouse report. | - - -## Split Keyboard Callbacks and Functions - -The combined functions below are only available when using `SPLIT_POINTING_ENABLE` and `POINTING_DEVICE_COMBINED`. The 2 callbacks `pointing_device_task_combined_*` replace the single sided equivalents above. See the [combined pointing devices example](feature_pointing_device#combined-pointing-devices) - -| Function | Description | -| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -| `pointing_device_set_shared_report(mouse_report)` | Sets the shared mouse report to the assigned `report_mouse_t` data structured passed to the function. | -| `pointing_device_set_cpi_on_side(bool, uint16_t)` | Sets the CPI/DPI of one side, if supported. Passing `true` will set the left and `false` the right | -| `pointing_device_combine_reports(left_report, right_report)` | Returns a combined mouse_report of left_report and right_report (as a `report_mouse_t` data structure) | -| `pointing_device_task_combined_kb(left_report, right_report)` | Callback, so keyboard code can intercept and modify the data. Returns a combined mouse report. | -| `pointing_device_task_combined_user(left_report, right_report)` | Callback, so user code can intercept and modify. Returns a combined mouse report using `pointing_device_combine_reports` | -| `pointing_device_adjust_by_defines_right(mouse_report)` | Applies right side rotations and invert configurations to a raw mouse report. | - - -# Manipulating Mouse Reports - -The report_mouse_t (here "mouseReport") has the following properties: - -* `mouseReport.x` - this is a signed int from -127 to 127 (not 128, this is defined in USB HID spec) representing movement (+ to the right, - to the left) on the x axis. -* `mouseReport.y` - this is a signed int from -127 to 127 (not 128, this is defined in USB HID spec) representing movement (+ upward, - downward) on the y axis. -* `mouseReport.v` - this is a signed int from -127 to 127 (not 128, this is defined in USB HID spec) representing vertical scrolling (+ upward, - downward). -* `mouseReport.h` - this is a signed int from -127 to 127 (not 128, this is defined in USB HID spec) representing horizontal scrolling (+ right, - left). -* `mouseReport.buttons` - this is a uint8_t in which all 8 bits are used. These bits represent the mouse button state - bit 0 is mouse button 1, and bit 7 is mouse button 8. - -To manually manipulate the mouse reports outside of the `pointing_device_task_*` functions, you can use: - -* `pointing_device_get_report()` - Returns the current report_mouse_t that represents the information sent to the host computer -* `pointing_device_set_report(report_mouse_t mouse_report)` - Overrides and saves the report_mouse_t to be sent to the host computer -* `pointing_device_send()` - Sends the mouse report to the host and zeroes out the report. - -When the mouse report is sent, the x, y, v, and h values are set to 0 (this is done in `pointing_device_send()`, which can be overridden to avoid this behavior). This way, button states persist, but movement will only occur once. For further customization, both `pointing_device_init` and `pointing_device_task` can be overridden. - -Additionally, by default, `pointing_device_send()` will only send a report when the report has actually changed. This prevents it from continuously sending mouse reports, which will keep the host system awake. This behavior can be changed by creating your own `pointing_device_send()` function. - -Also, you use the `has_mouse_report_changed(new_report, old_report)` function to check to see if the report has changed. - -## Examples - -### Custom Mouse Keycode - -In this example, a custom key is used to click the mouse and scroll 127 units vertically and horizontally, then undo all of that when released - because that's a totally useful function. - -```c -case MS_SPECIAL: - report_mouse_t currentReport = pointing_device_get_report(); - if (record->event.pressed) { - currentReport.v = 127; - currentReport.h = 127; - currentReport.buttons |= MOUSE_BTN1; // this is defined in report.h - } else { - currentReport.v = -127; - currentReport.h = -127; - currentReport.buttons &= ~MOUSE_BTN1; - } - pointing_device_set_report(currentReport); - pointing_device_send(); - break; -``` - -Recall that the mouse report is set to zero (except the buttons) whenever it is sent, so the scrolling would only occur once in each case. - -### Drag Scroll or Mouse Scroll - -A very common implementation is to use the mouse movement to scroll instead of moving the cursor on the system. This uses the `pointing_device_task_user` callback to intercept and modify the mouse report before it's sent to the host system. - -```c -enum custom_keycodes { - DRAG_SCROLL = SAFE_RANGE, -}; - -bool set_scrolling = false; - -report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) { - if (set_scrolling) { - mouse_report.h = mouse_report.x; - mouse_report.v = mouse_report.y; - mouse_report.x = 0; - mouse_report.y = 0; - } - return mouse_report; -} - -bool process_record_user(uint16_t keycode, keyrecord_t *record) { - if (keycode == DRAG_SCROLL && record->event.pressed) { - set_scrolling = !set_scrolling; - } - return true; -} -``` - -This allows you to toggle between scrolling and cursor movement by pressing the DRAG_SCROLL key. - -### Advanced Drag Scroll - -Sometimes, like with the Cirque trackpad, you will run into issues where the scrolling may be too fast. - -Here is a slightly more advanced example of drag scrolling. You will be able to change the scroll speed based on the values in set in `SCROLL_DIVISOR_H` and `SCROLL_DIVISOR_V`. This bit of code is also set up so that instead of toggling the scrolling state with set_scrolling = !set_scrolling, the set_scrolling variable is set directly to record->event.pressed. This way, the drag scrolling will only be active while the DRAG_SCROLL button is held down. - -```c -enum custom_keycodes { - DRAG_SCROLL = SAFE_RANGE, -}; - -bool set_scrolling = false; - -// Modify these values to adjust the scrolling speed -#define SCROLL_DIVISOR_H 8.0 -#define SCROLL_DIVISOR_V 8.0 - -// Variables to store accumulated scroll values -float scroll_accumulated_h = 0; -float scroll_accumulated_v = 0; - -// Function to handle mouse reports and perform drag scrolling -report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) { - // Check if drag scrolling is active - if (set_scrolling) { - // Calculate and accumulate scroll values based on mouse movement and divisors - scroll_accumulated_h += (float)mouse_report.x / SCROLL_DIVISOR_H; - scroll_accumulated_v += (float)mouse_report.y / SCROLL_DIVISOR_V; - - // Assign integer parts of accumulated scroll values to the mouse report - mouse_report.h = (int8_t)scroll_accumulated_h; - mouse_report.v = (int8_t)scroll_accumulated_v; - - // Update accumulated scroll values by subtracting the integer parts - scroll_accumulated_h -= (int8_t)scroll_accumulated_h; - scroll_accumulated_v -= (int8_t)scroll_accumulated_v; - - // Clear the X and Y values of the mouse report - mouse_report.x = 0; - mouse_report.y = 0; - } - return mouse_report; -} - -// Function to handle key events and enable/disable drag scrolling -bool process_record_user(uint16_t keycode, keyrecord_t *record) { - switch (keycode) { - case DRAG_SCROLL: - // Toggle set_scrolling when DRAG_SCROLL key is pressed or released - set_scrolling = record->event.pressed; - break; - default: - break; - } - return true; -} - -// Function to handle layer changes and disable drag scrolling when not in AUTO_MOUSE_DEFAULT_LAYER -layer_state_t layer_state_set_user(layer_state_t state) { - // Disable set_scrolling if the current layer is not the AUTO_MOUSE_DEFAULT_LAYER - if (get_highest_layer(state) != AUTO_MOUSE_DEFAULT_LAYER) { - set_scrolling = false; - } - return state; -} - -``` - - -## Split Examples - -The following examples make use the `SPLIT_POINTING_ENABLE` functionality and show how to manipulate the mouse report for a scrolling mode. - -### Single Pointing Device - -The following example will work with either `POINTING_DEVICE_LEFT` or `POINTING_DEVICE_RIGHT` and enables scrolling mode while on a particular layer. - -```c - -static bool scrolling_mode = false; - -layer_state_t layer_state_set_user(layer_state_t state) { - switch (get_highest_layer(state)) { - case _RAISE: // If we're on the _RAISE layer enable scrolling mode - scrolling_mode = true; - pointing_device_set_cpi(2000); - break; - default: - if (scrolling_mode) { // check if we were scrolling before and set disable if so - scrolling_mode = false; - pointing_device_set_cpi(8000); - } - break; - } - return state; -} - -report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) { - if (scrolling_mode) { - mouse_report.h = mouse_report.x; - mouse_report.v = mouse_report.y; - mouse_report.x = 0; - mouse_report.y = 0; - } - return mouse_report; -} -``` - -### Combined Pointing Devices - -The following example requires `POINTING_DEVICE_COMBINED` and sets the left side pointing device to scroll only. - -```c -void keyboard_post_init_user(void) { - pointing_device_set_cpi_on_side(true, 1000); //Set cpi on left side to a low value for slower scrolling. - pointing_device_set_cpi_on_side(false, 8000); //Set cpi on right side to a reasonable value for mousing. -} - -report_mouse_t pointing_device_task_combined_user(report_mouse_t left_report, report_mouse_t right_report) { - left_report.h = left_report.x; - left_report.v = left_report.y; - left_report.x = 0; - left_report.y = 0; - return pointing_device_combine_reports(left_report, right_report); -} -``` - -# Troubleshooting - -If you are having issues with pointing device drivers debug messages can be enabled that will give you insights in the inner workings. To enable these add to your keyboards `config.h` file: - -```c -#define POINTING_DEVICE_DEBUG -``` - -::: tip -The messages will be printed out to the `CONSOLE` output. For additional information, refer to [Debugging/Troubleshooting QMK](faq_debug). -::: - - ---- -# Automatic Mouse Layer {#pointing-device-auto-mouse} - -When using a pointing device combined with a keyboard the mouse buttons are often kept on a separate layer from the default keyboard layer, which requires pressing or holding a key to change layers before using the mouse. To make this easier and more efficient an additional pointing device feature may be enabled that will automatically activate a target layer as soon as the pointing device is active _(in motion, mouse button pressed etc.)_ and deactivate the target layer after a set time. - -Additionally if any key that is defined as a mouse key is pressed then the layer will be held as long as the key is pressed and the timer will be reset on key release. When a non-mouse key is pressed then the layer is deactivated early _(with some exceptions see below)_. Mod, mod tap, and one shot mod keys are ignored _(i.e. don't hold or activate layer but do not deactivate the layer either)_ when sending a modifier keycode _(e.g. hold for mod tap)_ allowing for mod keys to be used with the mouse without activating the target layer when typing. - -All of the standard layer keys (tap toggling, toggle, toggle on, one_shot, layer tap, layer mod) that activate the current target layer are uniquely handled to ensure they behave as expected _(see layer key table below)_. The target layer that can be changed at any point during by calling the `set_auto_mouse_layer();` function. - -### Behaviour of Layer keys that activate the target layer -| Layer key as in `keymap.c` | Auto Mouse specific behaviour | -| -------------------------- | --------------------------------------------------------------------------------------------------------------------- | -| `MO()` | Treated as a mouse key holding the layer while pressed | -| `LT()` | When tapped will be treated as non mouse key and mouse key when held | -| `LM()` | Treated as a mouse key | -| `TG()` | Will set flag preventing target layer deactivation or removal until pressed again | -| `TO()` | Same as `TG()` | -| `TT()` | Treated as a mouse key when `tap.count < TAPPING_TOGGLE` and as `TG` when `tap.count == TAPPING_TOGGLE` | -| `DF()` | Skips auto mouse key processing similar to mod keys | -| `OSL()` | Skips, but if current one shot layer is the target layer then it will prevent target layer deactivation or removal | - - -## How to enable: - -```c -// in config.h: -#define POINTING_DEVICE_AUTO_MOUSE_ENABLE -// only required if not setting mouse layer elsewhere -#define AUTO_MOUSE_DEFAULT_LAYER - -// in keymap.c: -void pointing_device_init_user(void) { - set_auto_mouse_layer(); // only required if AUTO_MOUSE_DEFAULT_LAYER is not set to index of - set_auto_mouse_enable(true); // always required before the auto mouse feature will work -} -``` - -Because the auto mouse feature can be disabled/enabled during runtime and starts as disabled by default it must be enabled by calling `set_auto_mouse_enable(true);` somewhere in firmware before the feature will work. -_Note: for setting the target layer during initialization either setting `AUTO_MOUSE_DEFAULT_LAYER` in `config.h` or calling `set_auto_mouse_layer()` can be used._ - - -## How to Customize: - -There are a few ways to control the auto mouse feature with both `config.h` options and functions for controlling it during runtime. - -### `config.h` Options: -| Define | Description | Range | Units | Default | -| ----------------------------------- | --------------------------------------------------------------------- | :------------------: | :---------: | -------------------------: | -| `POINTING_DEVICE_AUTO_MOUSE_ENABLE` | (Required) Enables auto mouse layer feature | | _None_ | _Not defined_ | -| `AUTO_MOUSE_DEFAULT_LAYER` | (Optional) Index of layer to use as default target layer | 0 - `LAYER_MAX` | _`uint8_t`_ | `1` | -| `AUTO_MOUSE_TIME` | (Optional) Time layer remains active after activation | _ideally_ (250-1000) | _ms_ | `650 ms` | -| `AUTO_MOUSE_DELAY` | (Optional) Lockout time after non-mouse key is pressed | _ideally_ (100-1000) | _ms_ | `TAPPING_TERM` or `200 ms` | -| `AUTO_MOUSE_DEBOUNCE` | (Optional) Time delay from last activation to next update | _ideally_ (10 - 100) | _ms_ | `25 ms` | -| `AUTO_MOUSE_THRESHOLD` | (Optional) Amount of mouse movement required to switch layers | 0 - | _units_ | `10 units` | - -### Adding mouse keys - -While all default mouse keys and layer keys(for current mouse layer) are treated as mouse keys, additional Keyrecords can be added to mouse keys by adding them to the is_mouse_record_* stack. - -#### Callbacks for setting up additional key codes as mouse keys: -| Callback | Description | -| -------------------------------------------------------------------- | -------------------------------------------------- | -| `bool is_mouse_record_kb(uint16_t keycode, keyrecord_t* record)` | keyboard level callback for adding mouse keys | -| `bool is_mouse_record_user(uint16_t keycode, keyrecord_t* record)` | user/keymap level callback for adding mouse keys | - -##### To use the callback function to add mouse keys: - -The following code will cause the enter key and all of the arrow keys to be treated as mouse keys (hold target layer while they are pressed and reset active layer timer). -```c - -// in .c: -bool is_mouse_record_kb(uint16_t keycode, keyrecord_t* record) { - switch(keycode) { - case KC_ENT: - return true; - case KC_RIGHT ... KC_UP: - return true; - default: - return false; - } - return is_mouse_record_user(keycode, record); -} -``` - - -## Advanced control - -There are several functions that allow for more advanced interaction with the auto mouse feature allowing for greater control. - -### Functions to control auto mouse enable and target layer: -| Function | Description | Aliases | Return type | -| :--------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------- | --------------: | -| `set_auto_mouse_enable(bool enable)` | Enable or disable auto mouse (true:enable, false:disable) | | `void`(None) | -| `get_auto_mouse_enable(void)` | Return auto mouse enable state (true:enabled, false:disabled) | `AUTO_MOUSE_ENABLED` | `bool` | -| `set_auto_mouse_layer(uint8_t LAYER)` | Change/set the target layer for auto mouse | | `void`(None) | -| `get_auto_mouse_layer(void)` | Return auto mouse target layer index | `AUTO_MOUSE_TARGET_LAYER` | `uint8_t` | -| `remove_auto_mouse_layer(layer_state_t state, bool force)` | Return `state` with target layer removed if appropriate (ignore criteria if `force`) | | `layer_state_t` | -| `auto_mouse_layer_off(void)` | Disable target layer if appropriate will call (makes call to `layer_state_set`) | | `void`(None) | -| `auto_mouse_toggle(void)` | Toggle on/off target toggle state (disables layer deactivation when true) | | `void`(None) | -| `get_auto_mouse_toggle(void)` | Return value of toggling state variable | | `bool` | -| `set_auto_mouse_timeout(uint16_t timeout)` | Change/set the timeout for turing off the layer | | `void`(None) | -| `get_auto_mouse_timeout(void)` | Return the current timeout for turing off the layer | | `uint16_t` | -| `set_auto_mouse_debounce(uint16_t timeout)` | Change/set the debounce for preventing layer activation | | `void`(None) | -| `get_auto_mouse_debounce(void)` | Return the current debounce for preventing layer activation | | `uint8_t` | -| `is_auto_mouse_active(void)` | Returns the active state of the auto mouse layer (eg if the layer has been triggered)| | `bool` | -| `get_auto_mouse_key_tracker(void)` | Gets the current count for the auto mouse key tracker. | | `int8_t` | -| `set_auto_mouse_key_tracker(int8_t key_tracker)` | Sets/Overrides the current count for the auto mouse key tracker. | | `void`(None) | - -_NOTES:_ - - _Due to the nature of how some functions work, the `auto_mouse_trigger_reset`, and `auto_mouse_layer_off` functions should never be called in the `layer_state_set_*` stack as this can cause indefinite loops._ - - _It is recommended that `remove_auto_mouse_layer` is used in the `layer_state_set_*` stack of functions and `auto_mouse_layer_off` is used everywhere else_ - - _`remove_auto_mouse_layer(state, false)` or `auto_mouse_layer_off()` should be called before any instance of `set_auto_mouse_enabled(false)` or `set_auto_mouse_layer(layer)` to ensure that the target layer will be removed appropriately before disabling auto mouse or changing target to avoid a stuck layer_ - -### Functions for handling custom key events: -| Function | Description | Return type | -| :--------------------------------------------------------- | -------------------------------------------------------------------------------- | --------------: | -| `auto_mouse_keyevent(bool pressed)` | Auto mouse mouse key event (true: key down, false: key up) | `void`(None) | -| `auto_mouse_trigger_reset(bool pressed)` | Reset auto mouse status on key down and start delay timer (non-mouse key event) | `void`(None) | -| `auto_mouse_toggle(void)` | Toggle on/off target toggle state (disables layer deactivation when true) | `void`(None) | -| `get_auto_mouse_toggle(void)` | Return value of toggling state variable | `bool` | -_NOTE: Generally it would be preferable to use the `is_mouse_record_*` functions to add any additional keys that should act as mouse keys rather than adding `auto_mouse_keyevent(record.event->pressed)` to `process_records_*`_ - -### Advanced control examples - -#### Disable auto mouse on certain layers: - -The auto mouse feature can be disabled any time and this can be helpful if you want to disable the auto mouse feature under certain circumstances such as when particular layers are active. One issue however is the handling of the target layer, it needs to be removed appropriately **before** disabling auto mouse _(see notes under control functions above)_. The following function would disable the auto_mouse feature whenever the layers `_LAYER5` through `_LAYER7` are active as the top most layer _(ignoring target layer)_. - -```c -// in keymap.c: -layer_state_t layer_state_set_user(layer_state_t state) { - // checks highest layer other than target layer - switch(get_highest_layer(remove_auto_mouse_layer(state, true))) { - case _LAYER5 ... _LAYER7: - // remove_auto_mouse_target must be called to adjust state *before* setting enable - state = remove_auto_mouse_layer(state, false); - set_auto_mouse_enable(false); - break; - default: - set_auto_mouse_enable(true); - break; - } - // recommend that any code that makes adjustment based on auto mouse layer state would go here - return state; -} -``` - -#### Set different target layer when a particular layer is active: - -The below code will change the auto mouse layer target to `_MOUSE_LAYER_2` when `_DEFAULT_LAYER_2` is highest default layer state. - -*NOTE: that `auto_mouse_layer_off` is used here instead of `remove_auto_mouse_layer` as `default_layer_state_set_*` stack is separate from the `layer_state_set_*` stack*, if something similar was to be done in `layer_state_set_user`, `state = remove_auto_mouse_layer(state, false)` should be used instead. - -*ADDITIONAL NOTE: `AUTO_MOUSE_TARGET_LAYER` is checked if already set to avoid deactivating the target layer unless needed*. - -```c -// in keymap.c -layer_state_t default_layer_state_set_user(layer_state_t state) { - // switch on change in default layer need to check if target layer already set to avoid turning off layer needlessly - switch(get_highest_layer(state)) { - case _DEFAULT_LAYER_2: - if ((AUTO_MOUSE_TARGET_LAYER) == _MOUSE_LAYER_2) break; - auto_mouse_layer_off(); - set_auto_mouse_layer(_MOUSE_LAYER_2); - break; - - default: - if((AUTO_MOUSE_TARGET_LAYER) == _MOUSE_LAYER_1) break; - auto_mouse_layer_off(); - set_auto_mouse_layer(_MOUSE_LAYER_1); - } - return state; -} -``` - -### Use custom keys to control auto mouse: -Custom key records could also be created that control the auto mouse feature. -The code example below would create a custom key that would toggle the auto mouse feature on and off when pressed while also setting a bool that could be used to disable other code that may turn it on such as the layer code above. - -```c -// in config.h: -enum user_custom_keycodes { - AM_Toggle = SAFE_RANGE -}; - -// in keymap.c: -// set up global bool to adjust other user code -bool auto_mouse_tg_off = !AUTO_MOUSE_ENABLED; - -bool process_record_user(uint16_t keycode, keyrecord_t* record) { - switch (keycode) { - // toggle auto mouse enable key - case AM_Toggle: - if(record->event.pressed) { // key down - auto_mouse_layer_off(); // disable target layer if needed - set_auto_mouse_enabled((AUTO_MOUSE_ENABLED) ^ 1); - auto_mouse_tg_off = !get_auto_mouse_enabled(); - } // do nothing on key up - return false; // prevent further processing of keycode - } -} -``` - - -## Customize Target Layer Activation - -Layer activation can be customized by overwriting the `auto_mouse_activation` function. This function is checked every time `pointing_device_task` is called when inactive and every `AUTO_MOUSE_DEBOUNCE` ms when active, and will evaluate pointing device level conditions that trigger target layer activation. When it returns true, the target layer will be activated barring the usual exceptions _(e.g. delay time has not expired)_. - -By default it will return true if any of the `mouse_report` axes `x`,`y`,`h`,`v` are non zero, or if there is any mouse buttons active in `mouse_report`. -_Note: The Cirque pinnacle track pad already implements a custom activation function that will activate on touchdown as well as movement all of the default conditions, currently this only works for the master side of split keyboards._ - -| Function | Description | Return type | -| :--------------------------------------------------------- | -------------------------------------------------------------------------------- | --------------: | -| `auto_mouse_activation(report_mouse_t mouse_report)` | Overwritable function that controls target layer activation (when true) | `bool` | - -## Auto Mouse for Custom Pointing Device Task - -When using a custom pointing device (overwriting `pointing_device_task`) the following code should be somewhere in the `pointing_device_task_*` stack: - -```c -bool pointing_device_task(void) { - //...Custom pointing device task code - - // handle automatic mouse layer (needs report_mouse_t as input) - pointing_device_task_auto_mouse(local_mouse_report); - - //...More custom pointing device task code - - return pointing_device_send(); -} -``` - -In general the following two functions must be implemented in appropriate locations for auto mouse to function: - -| Function | Description | Suggested location | -| -------------------------------------------------------------- | ------------------------------------------------------------ | ---------------------------: | -| `pointing_device_task_auto_mouse(report_mouse_t mouse_report)` | handles target layer activation and is_active status updates | `pointing_device_task` stack | -| `process_auto_mouse(uint16_t keycode, keyrecord_t* record)` | Keycode processing for auto mouse | `process_record` stack | diff --git a/docs/feature_programmable_button.md b/docs/feature_programmable_button.md deleted file mode 100644 index 1fdf44c29e..0000000000 --- a/docs/feature_programmable_button.md +++ /dev/null @@ -1,146 +0,0 @@ -# Programmable Button {#programmable-button} - -Programmable Buttons are keys that have no predefined meaning. This means they can be processed on the host side by custom software without the operating system trying to interpret them. - -The keycodes are emitted according to the HID Telephony Device page (`0x0B`), Programmable Button usage (`0x09`). On Linux (> 5.14) they are handled automatically and translated to `KEY_MACRO#` keycodes (up to `KEY_MACRO30`). - -::: tip -Currently there is no known support in Windows or macOS. It may be possible to write a custom HID driver to receive these usages, but this is out of the scope of the QMK documentation. -::: - -## Usage {#usage} - -Add the following to your `rules.mk`: - -```make -PROGRAMMABLE_BUTTON_ENABLE = yes -``` - -## Keycodes {#keycodes} - -|Key |Aliases|Description | -|---------------------------|-------|----------------------| -|`QK_PROGRAMMABLE_BUTTON_1` |`PB_1` |Programmable button 1 | -|`QK_PROGRAMMABLE_BUTTON_2` |`PB_2` |Programmable button 2 | -|`QK_PROGRAMMABLE_BUTTON_3` |`PB_3` |Programmable button 3 | -|`QK_PROGRAMMABLE_BUTTON_4` |`PB_4` |Programmable button 4 | -|`QK_PROGRAMMABLE_BUTTON_5` |`PB_5` |Programmable button 5 | -|`QK_PROGRAMMABLE_BUTTON_6` |`PB_6` |Programmable button 6 | -|`QK_PROGRAMMABLE_BUTTON_7` |`PB_7` |Programmable button 7 | -|`QK_PROGRAMMABLE_BUTTON_8` |`PB_8` |Programmable button 8 | -|`QK_PROGRAMMABLE_BUTTON_9` |`PB_9` |Programmable button 9 | -|`QK_PROGRAMMABLE_BUTTON_10`|`PB_10`|Programmable button 10| -|`QK_PROGRAMMABLE_BUTTON_11`|`PB_11`|Programmable button 11| -|`QK_PROGRAMMABLE_BUTTON_12`|`PB_12`|Programmable button 12| -|`QK_PROGRAMMABLE_BUTTON_13`|`PB_13`|Programmable button 13| -|`QK_PROGRAMMABLE_BUTTON_14`|`PB_14`|Programmable button 14| -|`QK_PROGRAMMABLE_BUTTON_15`|`PB_15`|Programmable button 15| -|`QK_PROGRAMMABLE_BUTTON_16`|`PB_16`|Programmable button 16| -|`QK_PROGRAMMABLE_BUTTON_17`|`PB_17`|Programmable button 17| -|`QK_PROGRAMMABLE_BUTTON_18`|`PB_18`|Programmable button 18| -|`QK_PROGRAMMABLE_BUTTON_19`|`PB_19`|Programmable button 19| -|`QK_PROGRAMMABLE_BUTTON_20`|`PB_20`|Programmable button 20| -|`QK_PROGRAMMABLE_BUTTON_21`|`PB_21`|Programmable button 21| -|`QK_PROGRAMMABLE_BUTTON_22`|`PB_22`|Programmable button 22| -|`QK_PROGRAMMABLE_BUTTON_23`|`PB_23`|Programmable button 23| -|`QK_PROGRAMMABLE_BUTTON_24`|`PB_24`|Programmable button 24| -|`QK_PROGRAMMABLE_BUTTON_25`|`PB_25`|Programmable button 25| -|`QK_PROGRAMMABLE_BUTTON_26`|`PB_26`|Programmable button 26| -|`QK_PROGRAMMABLE_BUTTON_27`|`PB_27`|Programmable button 27| -|`QK_PROGRAMMABLE_BUTTON_28`|`PB_28`|Programmable button 28| -|`QK_PROGRAMMABLE_BUTTON_29`|`PB_29`|Programmable button 29| -|`QK_PROGRAMMABLE_BUTTON_30`|`PB_30`|Programmable button 30| -|`QK_PROGRAMMABLE_BUTTON_31`|`PB_31`|Programmable button 31| -|`QK_PROGRAMMABLE_BUTTON_32`|`PB_32`|Programmable button 32| - -## API {#api} - -### `void programmable_button_clear(void)` {#api-programmable-button-clear} - -Clear the programmable button report. - ---- - -### `void programmable_button_add(uint8_t index)` {#api-programmable-button-add} - -Set the state of a button. - -#### Arguments {#api-programmable-button-add-arguments} - - - `uint8_t index` - The index of the button to press, from 0 to 31. - ---- - -### `void programmable_button_remove(uint8_t index)` {#api-programmable-button-remove} - -Reset the state of a button. - -#### Arguments {#api-programmable-button-remove-arguments} - - - `uint8_t index` - The index of the button to release, from 0 to 31. - ---- - -### `void programmable_button_register(uint8_t index)` {#api-programmable-button-register} - -Set the state of a button, and flush the report. - -#### Arguments {#api-programmable-button-register-arguments} - - - `uint8_t index` - The index of the button to press, from 0 to 31. - ---- - -### `void programmable_button_unregister(uint8_t index)` {#api-programmable-button-unregister} - -Reset the state of a button, and flush the report. - -#### Arguments {#api-programmable-button-unregister-arguments} - - - `uint8_t index` - The index of the button to release, from 0 to 31. - ---- - -### `bool programmable_button_is_on(uint8_t index)` {#api-programmable-button-is-on} - -Get the state of a button. - -#### Arguments {#api-programmable-button-is-on-arguments} - - - `uint8_t index` - The index of the button to check, from 0 to 31. - -#### Return Value {#api-programmable-button-is-on-return} - -`true` if the button is pressed. - ---- - -### `void programmable_button_flush(void)` {#api-programmable-button-flush} - -Send the programmable button report to the host. - ---- - -### `uint32_t programmable_button_get_report(void)` {#api-programmable-button-get-report} - -Get the programmable button report. - -#### Return Value {#api-programmable-button-get-report-return} - -The bitmask of programmable button states. - ---- - -### `void programmable_button_set_report(uint32_t report)` {#api-programmable-button-set-report} - -Set the programmable button report. - -#### Arguments {#api-programmable-button-set-report-arguments} - - - `uint32_t report` - A bitmask of programmable button states. diff --git a/docs/feature_ps2_mouse.md b/docs/feature_ps2_mouse.md deleted file mode 100644 index 90f4cca827..0000000000 --- a/docs/feature_ps2_mouse.md +++ /dev/null @@ -1,349 +0,0 @@ -# PS/2 Mouse Support {#ps2-mouse-support} - -Its possible to hook up a PS/2 mouse (for example touchpads or trackpoints) to your keyboard as a composite device. - -To hook up a Trackpoint, you need to obtain a Trackpoint module (i.e. harvest from a Thinkpad keyboard), identify the function of each pin of the module, and make the necessary circuitry between controller and Trackpoint module. For more information, please refer to [Trackpoint Hardware](https://deskthority.net/wiki/TrackPoint_Hardware) page on Deskthority Wiki. - -There are three available modes for hooking up PS/2 devices: USART (best), interrupts (better) or busywait (not recommended). - -## The Circuitry between Trackpoint and Controller {#the-circuitry-between-trackpoint-and-controller} - -To get the things working, a 4.7K drag is needed between the two lines DATA and CLK and the line 5+. - -``` - - DATA ----------+--------- PIN - | - 4.7K - | -MODULE 5+ --------+--+--------- PWR CONTROLLER - | - 4.7K - | - CLK ------+------------ PIN -``` - - -## Busywait Version {#busywait-version} - -Note: This is not recommended, you may encounter jerky movement or unsent inputs. Please use interrupt or USART version if possible. - -In rules.mk: - -```make -PS2_MOUSE_ENABLE = yes -PS2_ENABLE = yes -PS2_DRIVER = busywait -``` - -In your keyboard config.h: - -```c -#ifdef PS2_DRIVER_BUSYWAIT -# define PS2_CLOCK_PIN D1 -# define PS2_DATA_PIN D2 -#endif -``` - -### Interrupt Version (AVR/ATMega32u4) {#interrupt-version-avr} - -The following example uses D2 for clock and D5 for data. You can use any INT or PCINT pin for clock, and any pin for data. - -In rules.mk: - -```make -PS2_MOUSE_ENABLE = yes -PS2_ENABLE = yes -PS2_DRIVER = interrupt -``` - -In your keyboard config.h: - -```c -#ifdef PS2_DRIVER_INTERRUPT -#define PS2_CLOCK_PIN D2 -#define PS2_DATA_PIN D5 - -#define PS2_INT_INIT() do { \ - EICRA |= ((1<Z key types another "`z`." This is useful for -typing doubled letters, like the `z` in "`dazzle`": a double tap on Z -can instead be a roll from Z to Repeat, which is -potentially faster and more comfortable. The Repeat Key is also useful for -hotkeys, like repeating Ctrl + Shift + Right Arrow to select by word. - -Repeat Key remembers mods that were active with the last key press. These mods -are combined with any additional mods while pressing the Repeat Key. If the last -press key was Ctrl + Z, then Shift + -Repeat performs Ctrl + Shift + `Z`. - -## How do I enable Repeat Key - -In your `rules.mk`, add: - -```make -REPEAT_KEY_ENABLE = yes -``` - -Then pick a key in your keymap and assign it the keycode `QK_REPEAT_KEY` (short -alias `QK_REP`). Optionally, use the keycode `QK_ALT_REPEAT_KEY` (short alias -`QK_AREP`) on another key. - -## Keycodes - -|Keycode |Aliases |Description | -|-----------------------|---------|-------------------------------------| -|`QK_REPEAT_KEY` |`QK_REP` |Repeat the last pressed key | -|`QK_ALT_REPEAT_KEY` |`QK_AREP`|Perform alternate of the last key | - -## Alternate Repeating - -The Alternate Repeat Key performs the "alternate" action of the last pressed key -if it is defined. By default, Alternate Repeat is defined for navigation keys to -act in the reverse direction. When the last key is the common "select by word" -hotkey Ctrl + Shift + Right Arrow, the Alternate Repeat Key performs Ctrl + -Shift + Left Arrow, which together with the Repeat Key enables convenient -selection by words in either direction. - -Alternate Repeat is enabled with the Repeat Key by default. Optionally, to -reduce firmware size, Alternate Repeat may be disabled by adding in config.h: - -```c -#define NO_ALT_REPEAT_KEY -``` - -The following alternate keys are defined by default. See -`get_alt_repeat_key_keycode_user()` below for how to change or add to these -definitions. Where it makes sense, these definitions also include combinations -with mods, like Ctrl + Left ↔ Ctrl + Right Arrow. - -**Navigation** - -|Keycodes |Description | -|-----------------------------------|-----------------------------------| -|`KC_LEFT` ↔ `KC_RGHT` | Left ↔ Right Arrow | -|`KC_UP` ↔ `KC_DOWN` | Up ↔ Down Arrow | -|`KC_HOME` ↔ `KC_END` | Home ↔ End | -|`KC_PGUP` ↔ `KC_PGDN` | Page Up ↔ Page Down | -|`KC_MS_L` ↔ `KC_MS_R` | Mouse Cursor Left ↔ Right | -|`KC_MS_U` ↔ `KC_MS_D` | Mouse Cursor Up ↔ Down | -|`KC_WH_L` ↔ `KC_WH_R` | Mouse Wheel Left ↔ Right | -|`KC_WH_U` ↔ `KC_WH_D` | Mouse Wheel Up ↔ Down | - -**Misc** - -|Keycodes |Description | -|-----------------------------------|-----------------------------------| -|`KC_BSPC` ↔ `KC_DEL` | Backspace ↔ Delete | -|`KC_LBRC` ↔ `KC_RBRC` | `[` ↔ `]` | -|`KC_LCBR` ↔ `KC_RCBR` | `{` ↔ `}` | - -**Media** - -|Keycodes |Description | -|-----------------------------------|-----------------------------------| -|`KC_WBAK` ↔ `KC_WFWD` | Browser Back ↔ Forward | -|`KC_MNXT` ↔ `KC_MPRV` | Next ↔ Previous Media Track | -|`KC_MFFD` ↔ `KC_MRWD` | Fast Forward ↔ Rewind Media | -|`KC_VOLU` ↔ `KC_VOLD` | Volume Up ↔ Down | -|`KC_BRIU` ↔ `KC_BRID` | Brightness Up ↔ Down | - -**Hotkeys in Vim, Emacs, and other programs** - -|Keycodes |Description | -|-----------------------------------|-----------------------------------| -|mod + `KC_F` ↔ mod + `KC_B` | Forward ↔ Backward | -|mod + `KC_D` ↔ mod + `KC_U` | Down ↔ Up | -|mod + `KC_N` ↔ mod + `KC_P` | Next ↔ Previous | -|mod + `KC_A` ↔ mod + `KC_E` | Home ↔ End | -|mod + `KC_O` ↔ mod + `KC_I` | Vim jump list Older ↔ Newer | -|`KC_J` ↔ `KC_K` | Down ↔ Up | -|`KC_H` ↔ `KC_L` | Left ↔ Right | -|`KC_W` ↔ `KC_B` | Forward ↔ Backward by Word | - -(where above, "mod" is Ctrl, Alt, or GUI) - - -## Defining alternate keys - -Use the `get_alt_repeat_key_keycode_user()` callback to define the "alternate" -for additional keys or override the default definitions. For example, to define -Ctrl + Y as the alternate of Ctrl + Z, and vice versa, add the following in -keymap.c: - -```c -uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) { - if ((mods & MOD_MASK_CTRL)) { // Was Ctrl held? - switch (keycode) { - case KC_Y: return C(KC_Z); // Ctrl + Y reverses to Ctrl + Z. - case KC_Z: return C(KC_Y); // Ctrl + Z reverses to Ctrl + Y. - } - } - - return KC_TRNS; // Defer to default definitions. -} -``` - -The `keycode` and `mods` args are the keycode and mods that were active with the -last pressed key. The meaning of the return value from this function is: - -* `KC_NO` – do nothing (any predefined alternate key is not used); -* `KC_TRNS` – use the default alternate key if it exists; -* anything else – use the specified keycode. Any keycode may be returned - as an alternate key, including custom keycodes. - -Another example, defining Shift + Tab as the alternate of Tab, and vice versa: - -```c -uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) { - bool shifted = (mods & MOD_MASK_SHIFT); // Was Shift held? - switch (keycode) { - case KC_TAB: - if (shifted) { // If the last key was Shift + Tab, - return KC_TAB; // ... the reverse is Tab. - } else { // Otherwise, the last key was Tab, - return S(KC_TAB); // ... and the reverse is Shift + Tab. - } - } - - return KC_TRNS; -} -``` - -#### Eliminating SFBs - -Alternate Repeat can be configured more generally to perform an action that -"complements" the last key. Alternate Repeat is not limited to reverse -repeating, and it need not be symmetric. You can use it to eliminate cases of -same-finger bigrams in your layout, that is, pairs of letters typed by the same -finger. The following addresses the top 5 same-finger bigrams in English on -QWERTY, so that for instance "`ed`" may be typed as E, Alt -Repeat. - -```c -uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) { - switch (keycode) { - case KC_E: return KC_D; // For "ED" bigram. - case KC_D: return KC_E; // For "DE" bigram. - case KC_C: return KC_E; // For "CE" bigram. - case KC_L: return KC_O; // For "LO" bigram. - case KC_U: return KC_N; // For "UN" bigram. - } - - return KC_TRNS; -} -``` - -#### Typing shortcuts - -A useful possibility is having Alternate Repeat press [a -macro](feature_macros). This way macros can be used without having to -dedicate keys to them. The following defines a couple shortcuts. - -* Typing K, Alt Repeat produces "`keyboard`," with the - initial "`k`" typed as usual and the "`eybord`" produced by the macro. -* Typing ., Alt Repeat produces "`../`," handy for "up - directory" on the shell. Similary, . types the initial "`.`" and - "`./`" is produced by the macro. - -```c -enum custom_keycodes { - M_KEYBOARD = SAFE_RANGE, - M_UPDIR, - // Other custom keys... -}; - -uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) { - switch (keycode) { - case KC_K: return M_KEYBOARD; - case KC_DOT: return M_UPDIR; - } - - return KC_TRNS; -} - -bool process_record_user(uint16_t keycode, keyrecord_t* record) { - switch (keycode) { - case M_KEYBOARD: SEND_STRING(/*k*/"eyboard"); break; - case M_UPDIR: SEND_STRING(/*.*/"./"); break; - } - return true; -} -``` - -## Ignoring certain keys and mods - -In tracking what is "the last key" to be repeated or alternate repeated, -modifier and layer switch keys are always ignored. This makes it possible to set -some mods and change layers between pressing a key and repeating it. By default, -all other (non-modifier, non-layer switch) keys are remembered so that they are -eligible for repeating. To configure additional keys to be ignored, define -`remember_last_key_user()` in your keymap.c. - -#### Ignoring a key - -The following ignores the Backspace key: - -```c -bool remember_last_key_user(uint16_t keycode, keyrecord_t* record, - uint8_t* remembered_mods) { - switch (keycode) { - case KC_BSPC: - return false; // Ignore backspace. - } - - return true; // Other keys can be repeated. -} -``` - -Then for instance, the Repeat key in Left Arrow, -Backspace, Repeat sends Left Arrow again instead of -repeating Backspace. - -The `remember_last_key_user()` callback is called on every key press excluding -modifiers and layer switches. Returning true indicates the key is remembered, -while false means it is ignored. - -#### Filtering remembered mods - -The `remembered_mods` arg represents the mods that will be remembered with -this key. It can be modified to forget certain mods. This may be -useful to forget capitalization when repeating shifted letters, so that "Aaron" -does not becom "AAron": - -```c -bool remember_last_key_user(uint16_t keycode, keyrecord_t* record, - uint8_t* remembered_mods) { - // Forget Shift on letter keys when Shift or AltGr are the only mods. - switch (keycode) { - case KC_A ... KC_Z: - if ((*remembered_mods & ~(MOD_MASK_SHIFT | MOD_BIT(KC_RALT))) == 0) { - *remembered_mods &= ~MOD_MASK_SHIFT; - } - break; - } - - return true; -} -``` - -#### Further conditions - -Besides checking the keycode, this callback could also make conditions based on -the current layer state (with `IS_LAYER_ON(layer)`) or mods (`get_mods()`). For -example, the following ignores keys on layer 2 as well as key combinations -involving GUI: - -```c -bool remember_last_key_user(uint16_t keycode, keyrecord_t* record, - uint8_t* remembered_mods) { - if (IS_LAYER_ON(2) || (get_mods() & MOD_MASK_GUI)) { - return false; // Ignore layer 2 keys and GUI chords. - } - - return true; // Other keys can be repeated. -} -``` - -::: tip -See [Layer Functions](feature_layers#functions) and [Checking Modifier -::: -State](feature_advanced_keycodes#checking-modifier-state) for further -details. - - -## Handle how a key is repeated - -By default, pressing the Repeat Key will simply behave as if the last key -were pressed again. This also works with macro keys with custom handlers, -invoking the macro again. In case fine-tuning is needed for sensible repetition, -you can handle how a key is repeated with `get_repeat_key_count()` within -`process_record_user()`. - -The `get_repeat_key_count()` function returns a signed count of times the key -has been repeated or alternate repeated. When a key is pressed as usual, -`get_repeat_key_count()` is 0. On the first repeat, it is 1, then the second -repeat, 2, and so on. Negative counts are used similarly for alternate -repeating. For instance supposing `MY_MACRO` is a custom keycode used in the -layout: - -```c -bool process_record_user(uint16_t keycode, keyrecord_t* record) { - switch (keycode) { - case MY_MACRO: - if (get_repeat_key_count() > 0) { - // MY_MACRO is being repeated! - if (record->event.pressed) { - SEND_STRING("repeat!"); - } - } else { - // MY_MACRO is being used normally. - if (record->event.pressed) { - SEND_STRING("macro"); - } - } - return false; - - // Other macros... - } - return true; -} -``` - -## Handle how a key is alternate repeated - -Pressing the Alternate Repeat Key behaves as if the "alternate" of the last -pressed key were pressed, if an alternate is defined. To define how a particular -key is alternate repeated, use the `get_alt_repeat_key_keycode_user()` callback -as described above to define which keycode to use as its alternate. Beyond this, -`get_repeat_key_count()` may be used in custom handlers to fine-tune behavior -when alternate repeating. - -The following example defines `MY_MACRO` as its own alternate, and specially -handles repeating and alternate repeating: - -```c -uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) { - switch (keycode) { - case MY_MACRO: return MY_MACRO; // MY_MACRO is its own alternate. - } - return KC_TRNS; -} - -bool process_record_user(uint16_t keycode, keyrecord_t* record) { - switch (keycode) { - case MY_MACRO: - if (get_repeat_key_count() > 0) { // Repeating. - if (record->event.pressed) { - SEND_STRING("repeat!"); - } - } else if (get_repeat_key_count() < 0) { // Alternate repeating. - if (record->event.pressed) { - SEND_STRING("alt repeat!"); - } - } else { // Used normally. - if (record->event.pressed) { - SEND_STRING("macro"); - } - } - return false; - - // Other macros... - } - return true; -} -``` - - -## Functions - -| Function | Description | -|--------------------------------|------------------------------------------------------------------------| -| `get_last_keycode()` | The last key's keycode, the key to be repeated. | -| `get_last_mods()` | Mods to apply when repeating. | -| `set_last_keycode(kc)` | Set the keycode to be repeated. | -| `set_last_mods(mods)` | Set the mods to apply when repeating. | -| `get_repeat_key_count()` | Signed count of times the key has been repeated or alternate repeated. | -| `get_alt_repeat_key_keycode()` | Keycode to be used for alternate repeating. | - - -## Additional "Alternate" keys - -By leveraging `get_last_keycode()` in macros, it is possible to define -additional, distinct "Alternate Repeat"-like keys. The following defines two -keys `ALTREP2` and `ALTREP3` and implements ten shortcuts with them for common -English 5-gram letter patterns, taking inspiration from -[Stenotype](feature_stenography): - - -| Typing | Produces | Typing | Produces | -|----------------------------------|----------|----------------------------------|----------| -| A, ALTREP2 | `ation` | A, ALTREP3 | `about` | -| I, ALTREP2 | `ition` | I, ALTREP3 | `inter` | -| S, ALTREP2 | `ssion` | S, ALTREP3 | `state` | -| T, ALTREP2 | `their` | T, ALTREP3 | `there` | -| W, ALTREP2 | `which` | W, ALTREP3 | `would` | - -```c -enum custom_keycodes { - ALTREP2 = SAFE_RANGE, - ALTREP3, -}; - -// Use ALTREP2 and ALTREP3 in your layout... - -bool remember_last_key_user(uint16_t keycode, keyrecord_t* record, - uint8_t* remembered_mods) { - switch (keycode) { - case ALTREP2: - case ALTREP3: - return false; // Ignore ALTREP keys. - } - - return true; // Other keys can be repeated. -} - -static void process_altrep2(uint16_t keycode, uint8_t mods) { - switch (keycode) { - case KC_A: SEND_STRING(/*a*/"tion"); break; - case KC_I: SEND_STRING(/*i*/"tion"); break; - case KC_S: SEND_STRING(/*s*/"sion"); break; - case KC_T: SEND_STRING(/*t*/"heir"); break; - case KC_W: SEND_STRING(/*w*/"hich"); break; - } -} - -static void process_altrep3(uint16_t keycode, uint8_t mods) { - switch (keycode) { - case KC_A: SEND_STRING(/*a*/"bout"); break; - case KC_I: SEND_STRING(/*i*/"nter"); break; - case KC_S: SEND_STRING(/*s*/"tate"); break; - case KC_T: SEND_STRING(/*t*/"here"); break; - case KC_W: SEND_STRING(/*w*/"ould"); break; - } -} - -bool process_record_user(uint16_t keycode, keyrecord_t* record) { - switch (keycode) { - case ALTREP2: - if (record->event.pressed) { - process_altrep2(get_last_keycode(), get_last_mods()); - } - return false; - - case ALTREP3: - if (record->event.pressed) { - process_altrep3(get_last_keycode(), get_last_mods()); - } - return false; - } - - return true; -} -``` - diff --git a/docs/feature_rgb_matrix.md b/docs/feature_rgb_matrix.md deleted file mode 100644 index b9c01dcbf5..0000000000 --- a/docs/feature_rgb_matrix.md +++ /dev/null @@ -1,1119 +0,0 @@ -# RGB Matrix Lighting {#rgb-matrix-lighting} - -This feature allows you to use RGB LED matrices driven by external drivers. It hooks into the RGBLIGHT system so you can use the same keycodes as RGBLIGHT to control it. - -If you want to use single color LED's you should use the [LED Matrix Subsystem](feature_led_matrix) instead. - -## Driver configuration {#driver-configuration} ---- -### IS31FL3731 {#is31fl3731} - -There is basic support for addressable RGB matrix lighting with the I2C IS31FL3731 RGB controller. To enable it, add this to your `rules.mk`: - -```make -RGB_MATRIX_ENABLE = yes -RGB_MATRIX_DRIVER = is31fl3731 -``` - -You can use between 1 and 4 IS31FL3731 IC's. Do not specify `DRIVER_ADDR_` defines for IC's that are not present on your keyboard. You can define the following items in `config.h`: - -| Variable | Description | Default | -|----------|-------------|---------| -| `IS31FL3731_I2C_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 | -| `IS31FL3731_I2C_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 | -| `IS31FL3731_DEGHOST` | (Optional) Set this define to enable de-ghosting by halving Vcc during blanking time | | -| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | | -| `IS31FL3731_I2C_ADDRESS_1` | (Required) Address for the first RGB driver | | -| `IS31FL3731_I2C_ADDRESS_2` | (Optional) Address for the second RGB driver | | -| `IS31FL3731_I2C_ADDRESS_3` | (Optional) Address for the third RGB driver | | -| `IS31FL3731_I2C_ADDRESS_4` | (Optional) Address for the fourth RGB driver | | - -Here is an example using 2 drivers. - -```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) -// The address will vary depending on your wiring: -// 00 AD <-> GND -// 01 AD <-> SCL -// 10 AD <-> SDA -// 11 AD <-> VCC -// ADDR represents A1:A0 of the 7-bit address. -// The result is: 0b11101(ADDR) -#define IS31FL3731_I2C_ADDRESS_1 IS31FL3731_I2C_ADDRESS_GND -#define IS31FL3731_I2C_ADDRESS_2 IS31FL3731_I2C_ADDRESS_SDA - -#define DRIVER_1_LED_TOTAL 25 -#define DRIVER_2_LED_TOTAL 24 -#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL) -``` - -::: warning -Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`. -::: - -For split keyboards using `RGB_MATRIX_SPLIT` with an LED driver, you can either have the same driver address or different driver addresses. If using different addresses, use `IS31FL3731_I2C_ADDRESS_1` for one and `IS31FL3731_I2C_ADDRESS_2` for the other one. Then, in `g_is31fl3731_leds`, fill out the correct driver index (0 or 1). If using one address, use `IS31FL3731_I2C_ADDRESS_1` for both, and use index 0 for `g_is31fl3731_leds`. - -Define these arrays listing all the LEDs in your `.c`: - -```c -const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT] = { -/* Refer to IS31 manual for these locations - * driver - * | R location - * | | G location - * | | | B location - * | | | | */ - {0, C1_3, C2_3, C3_3}, - .... -} -``` - -Where `Cx_y` is the location of the LED in the matrix defined by [the datasheet](https://www.issi.com/WW/pdf/31FL3731.pdf) and the header file `drivers/led/issi/is31fl3731.h`. The `driver` is the index of the driver you defined in your `config.h` (`0`, `1`, `2`, or `3`). - ---- -### IS31FL3733 {#is31fl3733} - -There is basic support for addressable RGB matrix lighting with the I2C IS31FL3733 RGB controller. To enable it, add this to your `rules.mk`: - -```make -RGB_MATRIX_ENABLE = yes -RGB_MATRIX_DRIVER = is31fl3733 -``` - -You can use between 1 and 4 IS31FL3733 IC's. Do not specify `DRIVER_ADDR_` defines for IC's that are not present on your keyboard. You can define the following items in `config.h`: - -| Variable | Description | Default | -|----------|-------------|---------| -| `IS31FL3733_I2C_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 | -| `IS31FL3733_I2C_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 | -| `IS31FL3733_PWM_FREQUENCY` | (Optional) PWM Frequency Setting - IS31FL3733B only | 0 | -| `IS31FL3733_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF | -| `IS31FL3733_SWPULLUP` | (Optional) Set the value of the SWx lines on-chip de-ghosting resistors | PUR_0R (Disabled) | -| `IS31FL3733_CSPULLUP` | (Optional) Set the value of the CSx lines on-chip de-ghosting resistors | PUR_0R (Disabled) | -| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | | -| `IS31FL3733_I2C_ADDRESS_1` | (Required) Address for the first RGB driver | | -| `IS31FL3733_I2C_ADDRESS_2` | (Optional) Address for the second RGB driver | | -| `IS31FL3733_I2C_ADDRESS_3` | (Optional) Address for the third RGB driver | | -| `IS31FL3733_I2C_ADDRESS_4` | (Optional) Address for the fourth RGB driver | | -| `IS31FL3733_SYNC_1` | (Optional) Sync configuration for the first RGB driver | 0 | -| `IS31FL3733_SYNC_2` | (Optional) Sync configuration for the second RGB driver | 0 | -| `IS31FL3733_SYNC_3` | (Optional) Sync configuration for the third RGB driver | 0 | -| `IS31FL3733_SYNC_4` | (Optional) Sync configuration for the fourth RGB driver | 0 | - -The IS31FL3733 IC's have on-chip resistors that can be enabled to allow for de-ghosting of the RGB matrix. By default these resistors are not enabled (`IS31FL3733_SWPULLUP`/`IS31FL3733_CSPULLUP` are given the value of `IS31FL3733_PUR_0R`), the values that can be set to enable de-ghosting are as follows: - -| `IS31FL3733_SWPULLUP/IS31FL3733_CSPULLUP` | Description | -|----------------------|-------------| -| `IS31FL3733_PUR_0R` | (default) Do not use the on-chip resistors/enable de-ghosting | -| `IS31FL3733_PUR_05KR` | The 0.5k Ohm resistor used during blanking period (t_NOL) | -| `IS31FL3733_PUR_3KR` | The 3k Ohm resistor used at all times | -| `IS31FL3733_PUR_4KR` | The 4k Ohm resistor used at all times | -| `IS31FL3733_PUR_8KR` | The 8k Ohm resistor used at all times | -| `IS31FL3733_PUR_16KR` | The 16k Ohm resistor used at all times | -| `IS31FL3733_PUR_32KR` | The 32k Ohm resistor used during blanking period (t_NOL) | - -Here is an example using 2 drivers. - -```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) -// The address will vary depending on your wiring: -// 00 ADDRn <-> GND -// 01 ADDRn <-> SCL -// 10 ADDRn <-> SDA -// 11 ADDRn <-> VCC -// ADDR1 represents A1:A0 of the 7-bit address. -// ADDR2 represents A3:A2 of the 7-bit address. -// The result is: 0b101(ADDR2)(ADDR1) -#define IS31FL3733_I2C_ADDRESS_1 IS31FL3733_I2C_ADDRESS_GND_GND -#define IS31FL3733_I2C_ADDRESS_2 IS31FL3733_I2C_ADDRESS_GND_VCC - -#define DRIVER_1_LED_TOTAL 58 -#define DRIVER_2_LED_TOTAL 10 -#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL) -``` - -::: warning -Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`. -::: - -Currently only 4 drivers are supported, but it would be trivial to support all 8 combinations. - -Define these arrays listing all the LEDs in your `.c`: - -```c -const is31fl3733_led_t PROGMEM g_is31fl3733_leds[IS31FL3733_LED_COUNT] = { -/* Refer to IS31 manual for these locations - * driver - * | R location - * | | G location - * | | | B location - * | | | | */ - {0, SW1_CS1, SW1_CS2, SW1_CS3}, - .... -} -``` - -Where `SWx_CSy` is the location of the LED in the matrix defined by [the datasheet](https://www.issi.com/WW/pdf/31FL3733.pdf) and the header file `drivers/led/issi/is31fl3733.h`. The `driver` is the index of the driver you defined in your `config.h` (`0`, `1`, `2`, or `3` for now). - ---- -### IS31FL3736 {#is31fl3736} - -There is basic support for addressable RGB matrix lighting with the I2C IS31FL3736 RGB controller. To enable it, add this to your `rules.mk`: - -```make -RGB_MATRIX_ENABLE = yes -RGB_MATRIX_DRIVER = is31fl3736 -``` -You can use between 1 and 4 IS31FL3736 IC's. Do not specify `DRIVER_ADDR_` defines for IC's that are not present on your keyboard. - -Configure the hardware via your `config.h`: - -| Variable | Description | Default | -|----------|-------------|---------| -| `IS31FL3736_I2C_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 | -| `IS31FL3736_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 | -| `IS31FL3736_PWM_FREQUENCY` | (Optional) PWM Frequency Setting - IS31FL3736B only | 0 | -| `IS31FL3736_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF | -| `IS31FL3736_SWPULLUP` | (Optional) Set the value of the SWx lines on-chip de-ghosting resistors | PUR_0R (Disabled) | -| `IS31FL3736_CSPULLUP` | (Optional) Set the value of the CSx lines on-chip de-ghosting resistors | PUR_0R (Disabled) | -| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | | -| `IS31FL3736_I2C_ADDRESS_1` | (Required) Address for the first RGB driver | | -| `IS31FL3736_I2C_ADDRESS_2` | (Optional) Address for the second RGB driver | | -| `IS31FL3736_I2C_ADDRESS_3` | (Optional) Address for the third RGB driver | | -| `IS31FL3736_I2C_ADDRESS_4` | (Optional) Address for the fourth RGB driver | | - -The IS31FL3736 IC's have on-chip resistors that can be enabled to allow for de-ghosting of the RGB matrix. By default these resistors are not enabled (`IS31FL3736_SWPULLUP`/`IS31FL3736_CSPULLUP` are given the value of `IS31FL3736_PUR_0R`), the values that can be set to enable de-ghosting are as follows: - -| `IS31FL3736_SWPULLUP/IS31FL3736_CSPULLUP` | Description | -|----------------------|-------------| -| `IS31FL3736_PUR_0R` | (default) Do not use the on-chip resistors/enable de-ghosting | -| `IS31FL3736_PUR_05KR` | The 0.5k Ohm resistor used during blanking period (t_NOL) | -| `IS31FL3736_PUR_1KR` | The 1k Ohm resistor used during blanking period (t_NOL) | -| `IS31FL3736_PUR_2KR` | The 2k Ohm resistor used during blanking period (t_NOL) | -| `IS31FL3736_PUR_4KR` | The 4k Ohm resistor used during blanking period (t_NOL) | -| `IS31FL3736_PUR_8KR` | The 8k Ohm resistor during blanking period (t_NOL) | -| `IS31FL3736_PUR_16KR` | The 16k Ohm resistor during blanking period (t_NOL) | -| `IS31FL3736_PUR_32KR` | The 32k Ohm resistor used during blanking period (t_NOL) | - -Here is an example using 2 drivers. - -```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) -// The address will vary depending on your wiring: -// 00 ADDRn <-> GND -// 01 ADDRn <-> SCL -// 10 ADDRn <-> SDA -// 11 ADDRn <-> VCC -// ADDR1 represents A1:A0 of the 7-bit address. -// ADDR2 represents A3:A2 of the 7-bit address. -// The result is: 0b101(ADDR2)(ADDR1) -#define IS31FL3736_I2C_ADDRESS_1 IS31FL3736_I2C_ADDRESS_GND_GND -#define IS31FL3736_I2C_ADDRESS_2 IS31FL3736_I2C_ADDRESS_GND_SCL - -#define DRIVER_1_LED_TOTAL 30 -#define DRIVER_2_LED_TOTAL 32 -#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL) -``` -::: warning -Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`. -::: - -Define these arrays listing all the LEDs in your `.c`: - -```c -const is31fl3736_led_t PROGMEM g_is31fl3736_leds[IS31FL3736_LED_COUNT] = { -/* Refer to IS31 manual for these locations - * driver - * | R location - * | | G location - * | | | B location - * | | | | */ - {0, SW1_CS1, SW1_CS2, SW1_CS3}, - .... -} -``` -### IS31FL3737 {#is31fl3737} - -There is basic support for addressable RGB matrix lighting with the I2C IS31FL3737 RGB controller. To enable it, add this to your `rules.mk`: - -```make -RGB_MATRIX_ENABLE = yes -RGB_MATRIX_DRIVER = is31fl3737 -``` -You can use between 1 and 4 IS31FL3737 IC's. Do not specify `DRIVER_ADDR_` defines for IC's that are not present on your keyboard. - -Configure the hardware via your `config.h`: - -| Variable | Description | Default | -|----------|-------------|---------| -| `IS31FL3737_I2C_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 | -| `IS31FL3737_I2C_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 | -| `IS31FL3737_PWM_FREQUENCY` | (Optional) PWM Frequency Setting - IS31FL3737B only | 0 | -| `IS31FL3737_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF | -| `IS31FL3737_SWPULLUP` | (Optional) Set the value of the SWx lines on-chip de-ghosting resistors | PUR_0R (Disabled) | -| `IS31FL3737_CSPULLUP` | (Optional) Set the value of the CSx lines on-chip de-ghosting resistors | PUR_0R (Disabled) | -| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | | -| `IS31FL3737_I2C_ADDRESS_1` | (Required) Address for the first RGB driver | | -| `IS31FL3737_I2C_ADDRESS_2` | (Optional) Address for the second RGB driver | | -| `IS31FL3737_I2C_ADDRESS_3` | (Optional) Address for the third RGB driver | | -| `IS31FL3737_I2C_ADDRESS_4` | (Optional) Address for the fourth RGB driver | | - -The IS31FL3737 IC's have on-chip resistors that can be enabled to allow for de-ghosting of the RGB matrix. By default these resistors are not enabled (`IS31FL3737_SWPULLUP`/`IS31FL3737_CSPULLUP` are given the value of `IS31FL3737_PUR_0R`), the values that can be set to enable de-ghosting are as follows: - -| `IS31FL3737_SWPULLUP/IS31FL3737_CSPULLUP` | Description | -|----------------------|-------------| -| `IS31FL3737_PUR_0R` | (default) Do not use the on-chip resistors/enable de-ghosting | -| `IS31FL3737_PUR_05KR` | The 0.5k Ohm resistor used during blanking period (t_NOL) | -| `IS31FL3737_PUR_1KR` | The 1k Ohm resistor used during blanking period (t_NOL) | -| `IS31FL3737_PUR_2KR` | The 2k Ohm resistor used during blanking period (t_NOL) | -| `IS31FL3737_PUR_4KR` | The 4k Ohm resistor used during blanking period (t_NOL) | -| `IS31FL3737_PUR_8KR` | The 8k Ohm resistor during blanking period (t_NOL) | -| `IS31FL3737_PUR_16KR` | The 16k Ohm resistor during blanking period (t_NOL) | -| `IS31FL3737_PUR_32KR` | The 32k Ohm resistor used during blanking period (t_NOL) | - -Here is an example using 2 drivers. - -```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) -// The address will vary depending on your wiring: -// 0000 ADDR <-> GND -// 0101 ADDR <-> SCL -// 1010 ADDR <-> SDA -// 1111 ADDR <-> VCC -// ADDR represents A3:A0 of the 7-bit address. -// The result is: 0b101(ADDR) -#define IS31FL3737_I2C_ADDRESS_1 IS31FL3737_I2C_ADDRESS_GND -#define IS31FL3737_I2C_ADDRESS_2 IS31FL3737_I2C_ADDRESS_SCL - -#define DRIVER_1_LED_TOTAL 30 -#define DRIVER_2_LED_TOTAL 36 -#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL) -``` -::: warning -Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`. -::: - -Define these arrays listing all the LEDs in your `.c`: - -```c -const is31fl3737_led_t PROGMEM g_is31fl3737_leds[IS31FL3737_LED_COUNT] = { -/* Refer to IS31 manual for these locations - * driver - * | R location - * | | G location - * | | | B location - * | | | | */ - {0, SW1_CS1, SW1_CS2, SW1_CS3}, - .... -} -``` - -Where `SWx_CSy` 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/led/issi/is31fl3737.h`. The `driver` is the index of the driver you defined in your `config.h` (Only `0`, `1`, `2`, or `3` for now). - ---- -### IS31FLCOMMON {#is31flcommon} - -There is basic support for addressable RGB matrix lighting with a selection of I2C ISSI Lumissil RGB controllers through a shared common driver. To enable it, add this to your `rules.mk`: - -```makefile -RGB_MATRIX_ENABLE = yes -RGB_MATRIX_DRIVER = -``` - -Where `` is the applicable LED driver chip as below - -| Driver Name | Data Sheet | Capability | -|-------------|------------|------------| -| `IS31FL3742A` | [datasheet](https://www.lumissil.com/assets/pdf/core/IS31FL3742A_DS.pdf) | 60 RGB, 30x6 Matrix | -| `IS31FL3743A` | [datasheet](https://www.lumissil.com/assets/pdf/core/IS31FL3743A_DS.pdf) | 66 RGB, 18x11 Matrix | -| `IS31FL3745` | [datasheet](https://www.lumissil.com/assets/pdf/core/IS31FL3745_DS.pdf) | 48 RGB, 18x8 Matrix | -| `IS31FL3746A` | [datasheet](https://www.lumissil.com/assets/pdf/core/IS31FL3746A_DS.pdf) | 24 RGB, 18x4 Matrix | - -You can use between 1 and 4 IC's. Do not specify `DRIVER_ADDR_` define for IC's if not present on your keyboard. The `DRIVER_ADDR_1` default assumes that all Address pins on the controller have been connected to GND. Drivers that have SYNC functionality have the default settings to disable if 1 driver. If more than 1 drivers then `DRIVER_ADDR_1` will be set to Master and the remaining ones set to Slave. - -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 | -| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | | -| `DRIVER_ADDR_1` | (Optional) Address for the first RGB driver | | -| `DRIVER_ADDR_` | (Required) Address for the additional RGB drivers | | -| `ISSI_SSR_` | (Optional) Configuration for the Spread Spectrum Register | | -| `ISSI_CONFIGURATION` | (Optional) Configuration for the Configuration Register | | -| `ISSI_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF | -| `ISSI_PULLDOWNUP` | (Optional) Configuration for the Pull Up & Pull Down Register | | -| `ISSI_TEMP` | (Optional) Configuration for the Temperature Register | | -| `ISSI_PWM_ENABLE` | (Optional) Configuration for the PWM Enable Register | | -| `ISSI_PWM_SET` | (Optional) Configuration for the PWM Setting Register | | -| `ISSI_SCAL_RED` | (Optional) Configuration for the RED LEDs in Scaling Registers | 0xFF | -| `ISSI_SCAL_BLUE` | (Optional) Configuration for the BLUE LEDs in Scaling Registers | 0xFF | -| `ISSI_SCAL_GREEN` | (Optional) Configuration for the GREEN LEDs in Scaling Registers | 0xFF | -| `ISSI_MANUAL_SCALING` | (Optional) If you wish to configure the Scaling Registers manually | | - - -Defaults - -| Variable | IS31FL3742A | IS31FL3743A | IS31FL3745 | IS31FL3746 | -|----------|-------------|-------------|------------|------------| -| `DRIVER_ADDR_1` | 0b0110000 | 0b0100000 | 0b0100000 | 0b1100000 | -| `ISSI_SSR_1` | 0x00 | 0x00 / 0x60 | 0x00 / 0xC0 | 0x00 | -| `ISSI_SSR_<2-4>` | 0x00 | 0x40 | 0x80 | 0x00 | -| `ISSI_CONFIGURATION` | 0x31 | 0x01 | 0x31 | 0x01 | -| `ISSI_PULLDOWNUP` | 0x55 | 0x33 | 0x33 | 0x33 | -| `ISSI_TEMP` | N/A | 0x00 | 0x00 | 0x00 | -| `ISSI_PWM_ENABLE` | N/A | N/A | N/A | 0x00 | -| `ISSI_PWM_SET` | 0x00 | N/A | N/A | 0x00 | - -Here is an example using 2 drivers. - -```c -#define DRIVER_ADDR_2 0b0100001 - -#define DRIVER_1_LED_TOTAL 66 -#define DRIVER_2_LED_TOTAL 42 -#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL) -``` - -::: warning -Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`. -::: - -Currently only 4 drivers are supported, but it would be trivial to support for more. Note that using a combination of different drivers is not supported. All drivers must be of the same model. - -Define these arrays listing all the LEDs in your `.c`: - -```c -const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT] = { -/* Refer to IS31 manual for these locations - * driver - * | R location - * | | G location - * | | | B location - * | | | | */ - {0, SW1_CS1, SW1_CS2, SW1_CS3}, - .... -} -``` - -Where `CSx_SWx` is the location of the LED in the matrix defined by the datasheet. The `driver` is the index of the driver you defined in your `config.h` (`0`, `1`, `2`, or `3` for now). - -`ISSI_MANUAL_SCALING` is used to override the Scaling for individual LED's. By default they will be set as per `ISSI_SCAL_`. In `config.h` set how many LED's you want to manually set scaling for. -Eg `#define ISSI_MANUAL_SCALING 3` - -Then Define the array listing all the LEDs you want to override in your `.c`: - -```c -const is31_led PROGMEM g_is31_scaling[ISSI_MANUAL_SCALING] = { -/* LED Index - * | R scaling - * | | G scaling - * | | | B scaling - * | | | | */ - {5, 120, 155, 167}, - {9, 120, 155, 167}, - .... -} -``` - -Where LED Index is the position of the LED in the `g_is31_leds` array. The `scaling` value between 0 and 255 to be written to the Scaling Register. - ---- - -### WS2812 {#ws2812} - -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`: - -```make -RGB_MATRIX_ENABLE = yes -RGB_MATRIX_DRIVER = ws2812 -``` - -Configure the hardware via your `config.h`: - -```c -// The pin connected to the data pin of the LEDs -#define WS2812_DI_PIN D7 -// The number of LEDs connected -#define RGB_MATRIX_LED_COUNT 70 -``` - -::: tip -There are additional configuration options for ARM controllers that offer increased performance over the default bitbang driver. Please see [WS2812 Driver](ws2812_driver) for more information. -::: - ---- - -### APA102 {#apa102} - -There is basic support for APA102 based addressable LED strands. To enable it, add this to your `rules.mk`: - -```make -RGB_MATRIX_ENABLE = yes -RGB_MATRIX_DRIVER = apa102 -``` - -Configure the hardware via your `config.h`: - -```c -// The pin connected to the data pin of the LEDs -#define APA102_DI_PIN D7 -// The pin connected to the clock pin of the LEDs -#define APA102_CI_PIN D6 -// The number of LEDs connected -#define RGB_MATRIX_LED_COUNT 70 -``` - ---- -### AW20216S {#aw20216s} -There is basic support for addressable RGB matrix lighting with the SPI AW20216S RGB controller. To enable it, add this to your `rules.mk`: - -```make -RGB_MATRIX_ENABLE = yes -RGB_MATRIX_DRIVER = aw20216s -``` - -You can use up to 2 AW20216S IC's. Do not specify `DRIVER__xxx` defines for IC's that are not present on your keyboard. You can define the following items in `config.h`: - -| Variable | Description | Default | -|----------|-------------|---------| -| `AW20216S_CS_PIN_1` | (Required) MCU pin connected to first RGB driver chip select line | B13 | -| `AW20216S_CS_PIN_2` | (Optional) MCU pin connected to second RGB driver chip select line | | -| `AW20216S_EN_PIN_1` | (Required) MCU pin connected to first RGB driver hardware enable line | C13 | -| `AW20216S_EN_PIN_2` | (Optional) MCU pin connected to second RGB driver hardware enable line | | -| `DRIVER_1_LED_TOTAL` | (Required) How many RGB lights are connected to first RGB driver | | -| `DRIVER_2_LED_TOTAL` | (Optional) How many RGB lights are connected to second RGB driver | | -| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | | -| `AW20216S_SCALING_MAX` | (Optional) LED current scaling value (0-255, higher values mean LED is brighter at full PWM) | 150 | -| `AW20216S_GLOBAL_CURRENT_MAX` | (Optional) Driver global current limit (0-255, higher values means the driver may consume more power) | 150 | -| `AW20216S_SPI_MODE` | (Optional) Mode for SPI communication (0-3, defines polarity and phase of the clock) | 3 | -| `AW20216S_SPI_DIVISOR` | (Optional) Clock divisor for SPI communication (powers of 2, smaller numbers means faster communication, should not be less than 4) | 4 | - -Here is an example using 2 drivers. - -```c -#define AW20216S_CS_PIN_1 B13 -#define AW20216S_CS_PIN_2 B14 -// Hardware enable lines may be connected to the same pin -#define AW20216S_EN_PIN_1 C13 -#define AW20216S_EN_PIN_2 C13 - -#define DRIVER_1_LED_TOTAL 66 -#define DRIVER_2_LED_TOTAL 32 -#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL) -``` - -::: warning -Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`. -::: - -Define these arrays listing all the LEDs in your `.c`: - -```c -const aw20216s_led_t PROGMEM g_aw20216s_leds[AW20216S_LED_COUNT] = { -/* Each AW20216S channel is controlled by a register at some offset between 0x00 - * and 0xD7 inclusive. - * See drivers/led/aw20216s.h for the mapping between register offsets and - * driver pin locations. - * driver - * | R location - * | | G location - * | | | B location - * | | | | */ - { 0, SW1_CS1, SW1_CS2, SW1_CS3 }, - { 0, SW1_CS4, SW1_CS5, SW1_CS6 }, - { 0, SW1_CS7, SW1_CS8, SW1_CS9 }, - { 0, SW1_CS10, SW1_CS11, SW1_CS12 }, - { 0, SW1_CS13, SW1_CS14, SW1_CS15 }, - ... - { 1, SW1_CS1, SW1_CS2, SW1_CS3 }, - { 1, SW1_CS13, SW1_CS14, SW1_CS15 }, - { 1, SW1_CS16, SW1_CS17, SW1_CS18 }, - { 1, SW2_CS4, SW2_CS5, SW2_CS6 }, - ... -}; -``` - ---- - -## Common Configuration {#common-configuration} - -From this point forward the configuration is the same for all the drivers. The `led_config_t` struct provides a key electrical matrix to led index lookup table, what the physical position of each LED is on the board, and what type of key or usage the LED if the LED represents. Here is a brief example: - -```c -led_config_t g_led_config = { { - // Key Matrix to LED Index - { 5, NO_LED, NO_LED, 0 }, - { NO_LED, NO_LED, NO_LED, NO_LED }, - { 4, NO_LED, NO_LED, 1 }, - { 3, NO_LED, NO_LED, 2 } -}, { - // LED Index to Physical Position - { 188, 16 }, { 187, 48 }, { 149, 64 }, { 112, 64 }, { 37, 48 }, { 38, 16 } -}, { - // LED Index to Flag - 1, 4, 4, 4, 4, 1 -} }; -``` - -The first part, `// Key Matrix to LED Index`, tells the system what key this LED represents by using the key's electrical matrix row & col. The second part, `// LED Index to Physical Position` represents the LED's physical `{ x, y }` position on the keyboard. The default expected range of values for `{ x, y }` is the inclusive range `{ 0..224, 0..64 }`. This default expected range is due to effects that calculate the center of the keyboard for their animations. The easiest way to calculate these positions is imagine your keyboard is a grid, and the top left of the keyboard represents `{ x, y }` coordinate `{ 0, 0 }` and the bottom right of your keyboard represents `{ 224, 64 }`. Using this as a basis, you can use the following formula to calculate the physical position: - -```c -x = 224 / (NUMBER_OF_COLS - 1) * COL_POSITION -y = 64 / (NUMBER_OF_ROWS - 1) * ROW_POSITION -``` - -Where NUMBER_OF_COLS, NUMBER_OF_ROWS, COL_POSITION, & ROW_POSITION are all based on the physical layout of your keyboard, not the electrical layout. - -As mentioned earlier, the center of the keyboard by default is expected to be `{ 112, 32 }`, but this can be changed if you want to more accurately calculate the LED's physical `{ x, y }` positions. Keyboard designers can implement `#define RGB_MATRIX_CENTER { 112, 32 }` in their config.h file with the new center point of the keyboard, or where they want it to be allowing more possibilities for the `{ x, y }` values. Do note that the maximum value for x or y is 255, and the recommended maximum is 224 as this gives animations runoff room before they reset. - -`// LED Index to Flag` is a bitmask, whether or not a certain LEDs is of a certain type. It is recommended that LEDs are set to only 1 type. - -## Flags {#flags} - -|Define |Value |Description | -|----------------------------|------|-------------------------------------------------| -|`HAS_FLAGS(bits, flags)` |*n/a* |Evaluates to `true` if `bits` has all `flags` set| -|`HAS_ANY_FLAGS(bits, flags)`|*n/a* |Evaluates to `true` if `bits` has any `flags` set| -|`LED_FLAG_NONE` |`0x00`|If this LED has no flags | -|`LED_FLAG_ALL` |`0xFF`|If this LED has all flags | -|`LED_FLAG_MODIFIER` |`0x01`|If the LED is on a modifier key | -|`LED_FLAG_UNDERGLOW` |`0x02`|If the LED is for underglow | -|`LED_FLAG_KEYLIGHT` |`0x04`|If the LED is for key backlight | -|`LED_FLAG_INDICATOR` |`0x08`|If the LED is for keyboard state indication | - -## Keycodes {#keycodes} - -All RGB keycodes are currently shared with the RGBLIGHT system: - -|Key |Aliases |Description | -|-------------------|----------|--------------------------------------------------------------------------------------| -|`RGB_TOG` | |Toggle RGB lighting on or off | -|`RGB_MODE_FORWARD` |`RGB_MOD` |Cycle through modes, reverse direction when Shift is held | -|`RGB_MODE_REVERSE` |`RGB_RMOD`|Cycle through modes in reverse, forward direction when Shift is held | -|`RGB_HUI` | |Increase hue, decrease hue when Shift is held | -|`RGB_HUD` | |Decrease hue, increase hue when Shift is held | -|`RGB_SAI` | |Increase saturation, decrease saturation when Shift is held | -|`RGB_SAD` | |Decrease saturation, increase saturation when Shift is held | -|`RGB_VAI` | |Increase value (brightness), decrease value when Shift is held | -|`RGB_VAD` | |Decrease value (brightness), increase value when Shift is held | -|`RGB_SPI` | |Increase effect speed (does not support eeprom yet), decrease speed when Shift is held| -|`RGB_SPD` | |Decrease effect speed (does not support eeprom yet), increase speed when Shift is held| -|`RGB_MODE_PLAIN` |`RGB_M_P` |Static (no animation) mode | -|`RGB_MODE_BREATHE` |`RGB_M_B` |Breathing animation mode | -|`RGB_MODE_RAINBOW` |`RGB_M_R` |Full gradient scrolling left to right (uses the `RGB_MATRIX_CYCLE_LEFT_RIGHT` mode) | -|`RGB_MODE_SWIRL` |`RGB_M_SW`|Full gradient spinning pinwheel around center of keyboard (uses `RGB_MATRIX_CYCLE_PINWHEEL` mode) | - -* `RGB_MODE_*` keycodes will generally work, but not all of the modes are currently mapped to the correct effects for the RGB Matrix system. - -`RGB_MODE_PLAIN`, `RGB_MODE_BREATHE`, `RGB_MODE_RAINBOW`, and `RGB_MODE_SWIRL` are the only ones that are mapped properly. The rest don't have a direct equivalent, and are not mapped. - -::: tip -`RGB_*` keycodes cannot be used with functions like `tap_code16(RGB_HUD)` as they're not USB HID keycodes. If you wish to replicate similar behaviour in custom code within your firmware (e.g. inside `encoder_update_user()` or `process_record_user()`), the equivalent [RGB functions](#functions) should be used instead. -::: - - -::: warning -By default, if you have both the [RGB Light](feature_rgblight) and the RGB Matrix feature enabled, these keycodes will work for both features, at the same time. You can disable the keycode functionality by defining the `*_DISABLE_KEYCODES` option for the specific feature. -::: - -## RGB Matrix Effects {#rgb-matrix-effects} - -All effects have been configured to support current configuration values (Hue, Saturation, Value, & Speed) unless otherwise noted below. These are the effects that are currently available: - -```c -enum rgb_matrix_effects { - RGB_MATRIX_NONE = 0, - RGB_MATRIX_SOLID_COLOR = 1, // Static single hue, no speed support - RGB_MATRIX_ALPHAS_MODS, // Static dual hue, speed is hue for secondary hue - RGB_MATRIX_GRADIENT_UP_DOWN, // Static gradient top to bottom, speed controls how much gradient changes - RGB_MATRIX_GRADIENT_LEFT_RIGHT, // Static gradient left to right, speed controls how much gradient changes - RGB_MATRIX_BREATHING, // Single hue brightness cycling animation - RGB_MATRIX_BAND_SAT, // Single hue band fading saturation scrolling left to right - RGB_MATRIX_BAND_VAL, // Single hue band fading brightness scrolling left to right - RGB_MATRIX_BAND_PINWHEEL_SAT, // Single hue 3 blade spinning pinwheel fades saturation - RGB_MATRIX_BAND_PINWHEEL_VAL, // Single hue 3 blade spinning pinwheel fades brightness - RGB_MATRIX_BAND_SPIRAL_SAT, // Single hue spinning spiral fades saturation - RGB_MATRIX_BAND_SPIRAL_VAL, // Single hue spinning spiral fades brightness - RGB_MATRIX_CYCLE_ALL, // Full keyboard solid hue cycling through full gradient - RGB_MATRIX_CYCLE_LEFT_RIGHT, // Full gradient scrolling left to right - RGB_MATRIX_CYCLE_UP_DOWN, // Full gradient scrolling top to bottom - RGB_MATRIX_CYCLE_OUT_IN, // Full gradient scrolling out to in - RGB_MATRIX_CYCLE_OUT_IN_DUAL, // Full dual gradients scrolling out to in - RGB_MATRIX_RAINBOW_MOVING_CHEVRON, // Full gradient Chevron shapped scrolling left to right - RGB_MATRIX_CYCLE_PINWHEEL, // Full gradient spinning pinwheel around center of keyboard - RGB_MATRIX_CYCLE_SPIRAL, // Full gradient spinning spiral around center of keyboard - RGB_MATRIX_DUAL_BEACON, // Full gradient spinning around center of keyboard - RGB_MATRIX_RAINBOW_BEACON, // Full tighter gradient spinning around center of keyboard - RGB_MATRIX_RAINBOW_PINWHEELS, // Full dual gradients spinning two halfs of keyboard - RGB_MATRIX_FLOWER_BLOOMING, // Full tighter gradient of first half scrolling left to right and second half scrolling right to left - RGB_MATRIX_RAINDROPS, // Randomly changes a single key's hue - RGB_MATRIX_JELLYBEAN_RAINDROPS, // Randomly changes a single key's hue and saturation - RGB_MATRIX_HUE_BREATHING, // Hue shifts up a slight ammount at the same time, then shifts back - RGB_MATRIX_HUE_PENDULUM, // Hue shifts up a slight ammount in a wave to the right, then back to the left - RGB_MATRIX_HUE_WAVE, // Hue shifts up a slight ammount and then back down in a wave to the right - RGB_MATRIX_PIXEL_FRACTAL, // Single hue fractal filled keys pulsing horizontally out to edges - RGB_MATRIX_PIXEL_FLOW, // Pulsing RGB flow along LED wiring with random hues - RGB_MATRIX_PIXEL_RAIN, // Randomly light keys with random hues - RGB_MATRIX_TYPING_HEATMAP, // How hot is your WPM! - RGB_MATRIX_DIGITAL_RAIN, // That famous computer simulation - RGB_MATRIX_SOLID_REACTIVE_SIMPLE, // Pulses keys hit to hue & value then fades value out - RGB_MATRIX_SOLID_REACTIVE, // Static single hue, pulses keys hit to shifted hue then fades to current hue - RGB_MATRIX_SOLID_REACTIVE_WIDE, // Hue & value pulse near a single key hit then fades value out - RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE, // Hue & value pulse near multiple key hits then fades value out - RGB_MATRIX_SOLID_REACTIVE_CROSS, // Hue & value pulse the same column and row of a single key hit then fades value out - RGB_MATRIX_SOLID_REACTIVE_MULTICROSS, // Hue & value pulse the same column and row of multiple key hits then fades value out - RGB_MATRIX_SOLID_REACTIVE_NEXUS, // Hue & value pulse away on the same column and row of a single key hit then fades value out - RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS, // Hue & value pulse away on the same column and row of multiple key hits then fades value out - RGB_MATRIX_SPLASH, // Full gradient & value pulse away from a single key hit then fades value out - RGB_MATRIX_MULTISPLASH, // Full gradient & value pulse away from multiple key hits then fades value out - RGB_MATRIX_SOLID_SPLASH, // Hue & value pulse away from a single key hit then fades value out - RGB_MATRIX_SOLID_MULTISPLASH, // Hue & value pulse away from multiple key hits then fades value out - RGB_MATRIX_STARLIGHT, // LEDs turn on and off at random at varying brightness, maintaining user set color - RGB_MATRIX_STARLIGHT_DUAL_HUE, // LEDs turn on and off at random at varying brightness, modifies user set hue by +- 30 - RGB_MATRIX_STARLIGHT_DUAL_SAT, // LEDs turn on and off at random at varying brightness, modifies user set saturation by +- 30 - RGB_MATRIX_RIVERFLOW, // Modification to breathing animation, offset's animation depending on key location to simulate a river flowing - RGB_MATRIX_EFFECT_MAX -}; -``` - -You can enable a single effect by defining `ENABLE_[EFFECT_NAME]` in your `config.h`: - - -|Define |Description | -|------------------------------------------------------|----------------------------------------------| -|`#define ENABLE_RGB_MATRIX_ALPHAS_MODS` |Enables `RGB_MATRIX_ALPHAS_MODS` | -|`#define ENABLE_RGB_MATRIX_GRADIENT_UP_DOWN` |Enables `RGB_MATRIX_GRADIENT_UP_DOWN` | -|`#define ENABLE_RGB_MATRIX_GRADIENT_LEFT_RIGHT` |Enables `RGB_MATRIX_GRADIENT_LEFT_RIGHT` | -|`#define ENABLE_RGB_MATRIX_BREATHING` |Enables `RGB_MATRIX_BREATHING` | -|`#define ENABLE_RGB_MATRIX_BAND_SAT` |Enables `RGB_MATRIX_BAND_SAT` | -|`#define ENABLE_RGB_MATRIX_BAND_VAL` |Enables `RGB_MATRIX_BAND_VAL` | -|`#define ENABLE_RGB_MATRIX_BAND_PINWHEEL_SAT` |Enables `RGB_MATRIX_BAND_PINWHEEL_SAT` | -|`#define ENABLE_RGB_MATRIX_BAND_PINWHEEL_VAL` |Enables `RGB_MATRIX_BAND_PINWHEEL_VAL` | -|`#define ENABLE_RGB_MATRIX_BAND_SPIRAL_SAT` |Enables `RGB_MATRIX_BAND_SPIRAL_SAT` | -|`#define ENABLE_RGB_MATRIX_BAND_SPIRAL_VAL` |Enables `RGB_MATRIX_BAND_SPIRAL_VAL` | -|`#define ENABLE_RGB_MATRIX_CYCLE_ALL` |Enables `RGB_MATRIX_CYCLE_ALL` | -|`#define ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT` |Enables `RGB_MATRIX_CYCLE_LEFT_RIGHT` | -|`#define ENABLE_RGB_MATRIX_CYCLE_UP_DOWN` |Enables `RGB_MATRIX_CYCLE_UP_DOWN` | -|`#define ENABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON` |Enables `RGB_MATRIX_RAINBOW_MOVING_CHEVRON` | -|`#define ENABLE_RGB_MATRIX_CYCLE_OUT_IN` |Enables `RGB_MATRIX_CYCLE_OUT_IN` | -|`#define ENABLE_RGB_MATRIX_CYCLE_OUT_IN_DUAL` |Enables `RGB_MATRIX_CYCLE_OUT_IN_DUAL` | -|`#define ENABLE_RGB_MATRIX_CYCLE_PINWHEEL` |Enables `RGB_MATRIX_CYCLE_PINWHEEL` | -|`#define ENABLE_RGB_MATRIX_CYCLE_SPIRAL` |Enables `RGB_MATRIX_CYCLE_SPIRAL` | -|`#define ENABLE_RGB_MATRIX_DUAL_BEACON` |Enables `RGB_MATRIX_DUAL_BEACON` | -|`#define ENABLE_RGB_MATRIX_RAINBOW_BEACON` |Enables `RGB_MATRIX_RAINBOW_BEACON` | -|`#define ENABLE_RGB_MATRIX_RAINBOW_PINWHEELS` |Enables `RGB_MATRIX_RAINBOW_PINWHEELS` | -|`#define ENABLE_RGB_MATRIX_FLOWER_BLOOMING` |Enables `RGB_MATRIX_FLOWER_BLOOMING` | -|`#define ENABLE_RGB_MATRIX_RAINDROPS` |Enables `RGB_MATRIX_RAINDROPS` | -|`#define ENABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS` |Enables `RGB_MATRIX_JELLYBEAN_RAINDROPS` | -|`#define ENABLE_RGB_MATRIX_HUE_BREATHING` |Enables `RGB_MATRIX_HUE_BREATHING` | -|`#define ENABLE_RGB_MATRIX_HUE_PENDULUM` |Enables `RGB_MATRIX_HUE_PENDULUM` | -|`#define ENABLE_RGB_MATRIX_HUE_WAVE` |Enables `RGB_MATRIX_HUE_WAVE ` | -|`#define ENABLE_RGB_MATRIX_PIXEL_FRACTAL` |Enables `RGB_MATRIX_PIXEL_FRACTAL` | -|`#define ENABLE_RGB_MATRIX_PIXEL_FLOW` |Enables `RGB_MATRIX_PIXEL_FLOW` | -|`#define ENABLE_RGB_MATRIX_PIXEL_RAIN` |Enables `RGB_MATRIX_PIXEL_RAIN` | -|`#define ENABLE_RGB_MATRIX_STARLIGHT` |Enables `RGB_MATRIX_STARLIGHT` | -|`#define ENABLE_RGB_MATRIX_STARLIGHT_DUAL_HUE` |Enables `RGB_MATRIX_STARLIGHT_DUAL_HUE` | -|`#define ENABLE_RGB_MATRIX_STARLIGHT_DUAL_SAT` |Enables `RGB_MATRIX_STARLIGHT_DUAL_SAT` | -|`#define ENABLE_RGB_MATRIX_RIVERFLOW` |Enables `RGB_MATRIX_RIVERFLOW` | - -|Framebuffer Defines |Description | -|------------------------------------------------------|----------------------------------------------| -|`#define ENABLE_RGB_MATRIX_TYPING_HEATMAP` |Enables `RGB_MATRIX_TYPING_HEATMAP` | -|`#define ENABLE_RGB_MATRIX_DIGITAL_RAIN` |Enables `RGB_MATRIX_DIGITAL_RAIN` | - -::: tip -These modes introduce additional logic that can increase firmware size. -::: - -|Reactive Defines |Description | -|------------------------------------------------------|----------------------------------------------| -|`#define ENABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE` |Enables `RGB_MATRIX_SOLID_REACTIVE_SIMPLE` | -|`#define ENABLE_RGB_MATRIX_SOLID_REACTIVE` |Enables `RGB_MATRIX_SOLID_REACTIVE` | -|`#define ENABLE_RGB_MATRIX_SOLID_REACTIVE_WIDE` |Enables `RGB_MATRIX_SOLID_REACTIVE_WIDE` | -|`#define ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE` |Enables `RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE` | -|`#define ENABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS` |Enables `RGB_MATRIX_SOLID_REACTIVE_CROSS` | -|`#define ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTICROSS` |Enables `RGB_MATRIX_SOLID_REACTIVE_MULTICROSS`| -|`#define ENABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS` |Enables `RGB_MATRIX_SOLID_REACTIVE_NEXUS` | -|`#define ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS` |Enables `RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS`| -|`#define ENABLE_RGB_MATRIX_SPLASH` |Enables `RGB_MATRIX_SPLASH` | -|`#define ENABLE_RGB_MATRIX_MULTISPLASH` |Enables `RGB_MATRIX_MULTISPLASH` | -|`#define ENABLE_RGB_MATRIX_SOLID_SPLASH` |Enables `RGB_MATRIX_SOLID_SPLASH` | -|`#define ENABLE_RGB_MATRIX_SOLID_MULTISPLASH` |Enables `RGB_MATRIX_SOLID_MULTISPLASH` | - -::: tip -These modes introduce additional logic that can increase firmware size. -::: - - -### RGB Matrix Effect Typing Heatmap {#rgb-matrix-effect-typing-heatmap} - -This effect will color the RGB matrix according to a heatmap of recently pressed keys. Whenever a key is pressed its "temperature" increases as well as that of its neighboring keys. The temperature of each key is then decreased automatically every 25 milliseconds by default. - -In order to change the delay of temperature decrease define `RGB_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS`: - -```c -#define RGB_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS 50 -``` - -As heatmap uses the physical position of the leds set in the g_led_config, you may need to tweak the following options to get the best effect for your keyboard. Note the size of this grid is `224x64`. - -Limit the distance the effect spreads to surrounding keys. - -```c -#define RGB_MATRIX_TYPING_HEATMAP_SPREAD 40 -``` - -Limit how hot surrounding keys get from each press. - -```c -#define RGB_MATRIX_TYPING_HEATMAP_AREA_LIMIT 16 -``` - -Remove the spread effect entirely. - -```c -#define RGB_MATRIX_TYPING_HEATMAP_SLIM -``` - -It's also possible to adjust the tempo of *heating up*. It's defined as the number of shades that are -increased on the [HSV scale](https://en.wikipedia.org/wiki/HSL_and_HSV). Decreasing this value increases -the number of keystrokes needed to fully heat up the key. - -```c -#define RGB_MATRIX_TYPING_HEATMAP_INCREASE_STEP 32 -``` - -### RGB Matrix Effect Solid Reactive {#rgb-matrix-effect-solid-reactive} - -Solid reactive effects will pulse RGB light on key presses with user configurable hues. To enable gradient mode that will automatically change reactive color, add the following define: - -```c -#define RGB_MATRIX_SOLID_REACTIVE_GRADIENT_MODE -``` - -Gradient mode will loop through the color wheel hues over time and its duration can be controlled with the effect speed keycodes (`RGB_SPI`/`RGB_SPD`). - -## Custom RGB Matrix Effects {#custom-rgb-matrix-effects} - -By setting `RGB_MATRIX_CUSTOM_USER = yes` in `rules.mk`, new effects can be defined directly from your keymap or userspace, without having to edit any QMK core files. To declare new effects, create a `rgb_matrix_user.inc` file in the user keymap directory or userspace folder. - -::: tip -Hardware maintainers who want to limit custom effects to a specific keyboard can create a `rgb_matrix_kb.inc` file in the root of the keyboard directory, and add `RGB_MATRIX_CUSTOM_KB = yes` to the keyboard level `rules.mk`. -::: - -To use custom effects in your code, simply prepend `RGB_MATRIX_CUSTOM_` to the effect name specified in `RGB_MATRIX_EFFECT()`. For example, an effect declared as `RGB_MATRIX_EFFECT(my_cool_effect)` would be referenced with: - -```c -rgb_matrix_mode(RGB_MATRIX_CUSTOM_my_cool_effect); -``` - -```c -// !!! DO NOT ADD #pragma once !!! // - -// Step 1. -// Declare custom effects using the RGB_MATRIX_EFFECT macro -// (note the lack of semicolon after the macro!) -RGB_MATRIX_EFFECT(my_cool_effect) -RGB_MATRIX_EFFECT(my_cool_effect2) - -// Step 2. -// Define effects inside the `RGB_MATRIX_CUSTOM_EFFECT_IMPLS` ifdef block -#ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS - -// e.g: A simple effect, self-contained within a single method -static bool my_cool_effect(effect_params_t* params) { - RGB_MATRIX_USE_LIMITS(led_min, led_max); - for (uint8_t i = led_min; i < led_max; i++) { - rgb_matrix_set_color(i, 0xff, 0xff, 0x00); - } - return rgb_matrix_check_finished_leds(led_max); -} - -// e.g: A more complex effect, relying on external methods and state, with -// dedicated init and run methods -static uint8_t some_global_state; -static void my_cool_effect2_complex_init(effect_params_t* params) { - some_global_state = 1; -} -static bool my_cool_effect2_complex_run(effect_params_t* params) { - RGB_MATRIX_USE_LIMITS(led_min, led_max); - for (uint8_t i = led_min; i < led_max; i++) { - rgb_matrix_set_color(i, 0xff, some_global_state++, 0xff); - } - return rgb_matrix_check_finished_leds(led_max); -} -static bool my_cool_effect2(effect_params_t* params) { - if (params->init) my_cool_effect2_complex_init(params); - return my_cool_effect2_complex_run(params); -} - -#endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS -``` - -For inspiration and examples, check out the built-in effects under `quantum/rgb_matrix/animations/`. - - -## Colors {#colors} - -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_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 {#additional-configh-options} - -```c -#define RGB_MATRIX_KEYRELEASES // reactive effects respond to keyreleases (instead of keypresses) -#define RGB_MATRIX_TIMEOUT 0 // number of milliseconds to wait until rgb automatically turns off -#define RGB_MATRIX_SLEEP // turn off effects when suspended -#define RGB_MATRIX_LED_PROCESS_LIMIT (RGB_MATRIX_LED_COUNT + 4) / 5 // limits the number of LEDs to process in an animation per task run (increases keyboard responsiveness) -#define RGB_MATRIX_LED_FLUSH_LIMIT 16 // limits in milliseconds how frequently an animation will update the LEDs. 16 (16ms) is equivalent to limiting to 60fps (increases keyboard responsiveness) -#define RGB_MATRIX_MAXIMUM_BRIGHTNESS 200 // limits maximum brightness of LEDs to 200 out of 255. If not defined maximum brightness is set to 255 -#define RGB_MATRIX_DEFAULT_ON true // Sets the default enabled state, if none has been set -#define RGB_MATRIX_DEFAULT_MODE RGB_MATRIX_CYCLE_LEFT_RIGHT // Sets the default mode, if none has been set -#define RGB_MATRIX_DEFAULT_HUE 0 // Sets the default hue value, if none has been set -#define RGB_MATRIX_DEFAULT_SAT 255 // Sets the default saturation value, if none has been set -#define RGB_MATRIX_DEFAULT_VAL RGB_MATRIX_MAXIMUM_BRIGHTNESS // Sets the default brightness value, if none has been set -#define RGB_MATRIX_DEFAULT_SPD 127 // Sets the default animation speed, if none has been set -#define RGB_MATRIX_DEFAULT_FLAGS LED_FLAG_ALL // Sets the default LED flags, if none has been set -#define RGB_MATRIX_DISABLE_KEYCODES // disables control of rgb matrix by keycodes (must use code functions to control the feature) -#define RGB_MATRIX_SPLIT { X, Y } // (Optional) For split keyboards, the number of LEDs connected on each half. X = left, Y = Right. - // If reactive effects are enabled, you also will want to enable SPLIT_TRANSPORT_MIRROR -#define RGB_TRIGGER_ON_KEYDOWN // Triggers RGB keypress events on key down. This makes RGB control feel more responsive. This may cause RGB to not function properly on some boards -``` - -## EEPROM storage {#eeprom-storage} - -The EEPROM for it is currently shared with the LED Matrix system (it's generally assumed only one feature would be used at a time). - -## Functions {#functions} - -### Direct Operation {#direct-operation} -|Function |Description | -|--------------------------------------------|-------------| -|`rgb_matrix_set_color_all(r, g, b)` |Set all of the LEDs to the given RGB value, where `r`/`g`/`b` are between 0 and 255 (not written to EEPROM) | -|`rgb_matrix_set_color(index, r, g, b)` |Set a single LED to the given RGB value, where `r`/`g`/`b` are between 0 and 255, and `index` is between 0 and `RGB_MATRIX_LED_COUNT` (not written to EEPROM) | - -### Disable/Enable Effects {#disable-enable-effects} -|Function |Description | -|--------------------------------------------|-------------| -|`rgb_matrix_toggle()` |Toggle effect range LEDs between on and off | -|`rgb_matrix_toggle_noeeprom()` |Toggle effect range LEDs between on and off (not written to EEPROM) | -|`rgb_matrix_enable()` |Turn effect range LEDs on, based on their previous state | -|`rgb_matrix_enable_noeeprom()` |Turn effect range LEDs on, based on their previous state (not written to EEPROM) | -|`rgb_matrix_disable()` |Turn effect range LEDs off, based on their previous state | -|`rgb_matrix_disable_noeeprom()` |Turn effect range LEDs off, based on their previous state (not written to EEPROM) | - -### Change Effect Mode {#change-effect-mode} -|Function |Description | -|--------------------------------------------|-------------| -|`rgb_matrix_mode(mode)` |Set the mode, if RGB animations are enabled | -|`rgb_matrix_mode_noeeprom(mode)` |Set the mode, if RGB animations are enabled (not written to EEPROM) | -|`rgb_matrix_step()` |Change the mode to the next RGB animation in the list of enabled RGB animations | -|`rgb_matrix_step_noeeprom()` |Change the mode to the next RGB animation in the list of enabled RGB animations (not written to EEPROM) | -|`rgb_matrix_step_reverse()` |Change the mode to the previous RGB animation in the list of enabled RGB animations | -|`rgb_matrix_step_reverse_noeeprom()` |Change the mode to the previous RGB animation in the list of enabled RGB animations (not written to EEPROM) | -|`rgb_matrix_increase_speed()` |Increase the speed of the animations | -|`rgb_matrix_increase_speed_noeeprom()` |Increase the speed of the animations (not written to EEPROM) | -|`rgb_matrix_decrease_speed()` |Decrease the speed of the animations | -|`rgb_matrix_decrease_speed_noeeprom()` |Decrease the speed of the animations (not written to EEPROM) | -|`rgb_matrix_set_speed(speed)` |Set the speed of the animations to the given value where `speed` is between 0 and 255 | -|`rgb_matrix_set_speed_noeeprom(speed)` |Set the speed of the animations to the given value where `speed` is between 0 and 255 (not written to EEPROM) | -|`rgb_matrix_reload_from_eeprom()` |Reload the effect configuration (enabled, mode and color) from EEPROM | - -### Change Color {#change-color} -|Function |Description | -|--------------------------------------------|-------------| -|`rgb_matrix_increase_hue()` |Increase the hue for effect range LEDs. This wraps around at maximum hue | -|`rgb_matrix_increase_hue_noeeprom()` |Increase the hue for effect range LEDs. This wraps around at maximum hue (not written to EEPROM) | -|`rgb_matrix_decrease_hue()` |Decrease the hue for effect range LEDs. This wraps around at minimum hue | -|`rgb_matrix_decrease_hue_noeeprom()` |Decrease the hue for effect range LEDs. This wraps around at minimum hue (not written to EEPROM) | -|`rgb_matrix_increase_sat()` |Increase the saturation for effect range LEDs. This wraps around at maximum saturation | -|`rgb_matrix_increase_sat_noeeprom()` |Increase the saturation for effect range LEDs. This wraps around at maximum saturation (not written to EEPROM) | -|`rgb_matrix_decrease_sat()` |Decrease the saturation for effect range LEDs. This wraps around at minimum saturation | -|`rgb_matrix_decrease_sat_noeeprom()` |Decrease the saturation for effect range LEDs. This wraps around at minimum saturation (not written to EEPROM) | -|`rgb_matrix_increase_val()` |Increase the value for effect range LEDs. This wraps around at maximum value | -|`rgb_matrix_increase_val_noeeprom()` |Increase the value for effect range LEDs. This wraps around at maximum value (not written to EEPROM) | -|`rgb_matrix_decrease_val()` |Decrease the value for effect range LEDs. This wraps around at minimum value | -|`rgb_matrix_decrease_val_noeeprom()` |Decrease the value for effect range LEDs. This wraps around at minimum value (not written to EEPROM) | -|`rgb_matrix_sethsv(h, s, v)` |Set LEDs to the given HSV value where `h`/`s`/`v` are between 0 and 255 | -|`rgb_matrix_sethsv_noeeprom(h, s, v)` |Set LEDs to the given HSV value where `h`/`s`/`v` are between 0 and 255 (not written to EEPROM) | - -### Query Current Status {#query-current-status} -|Function |Description | -|---------------------------------|---------------------------| -|`rgb_matrix_is_enabled()` |Gets current on/off status | -|`rgb_matrix_get_mode()` |Gets current mode | -|`rgb_matrix_get_hue()` |Gets current hue | -|`rgb_matrix_get_sat()` |Gets current sat | -|`rgb_matrix_get_val()` |Gets current val | -|`rgb_matrix_get_hsv()` |Gets hue, sat, and val and returns a [`HSV` structure](https://github.com/qmk/qmk_firmware/blob/7ba6456c0b2e041bb9f97dbed265c5b8b4b12192/quantum/color.h#L56-L61)| -|`rgb_matrix_get_speed()` |Gets current speed | -|`rgb_matrix_get_suspend_state()` |Gets current suspend state | - -## Callbacks {#callbacks} - -### Indicators {#indicators} - -If you want to set custom indicators, such as an LED for Caps Lock, or layer indication, then you can use the `rgb_matrix_indicators_kb` function on the keyboard level source file, or `rgb_matrix_indicators_user` function in the user `keymap.c`. -```c -bool rgb_matrix_indicators_kb(void) { - if (!rgb_matrix_indicators_user()) { - return false; - } - rgb_matrix_set_color(index, red, green, blue); - return true; -} -``` - -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 -bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) { - RGB_MATRIX_INDICATOR_SET_COLOR(index, red, green, blue); - return false; -} -``` - -### Indicator Examples {#indicator-examples} - -Caps Lock indicator on alphanumeric flagged keys: -```c -bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) { - if (host_keyboard_led_state().caps_lock) { - for (uint8_t i = led_min; i < led_max; i++) { - if (g_led_config.flags[i] & LED_FLAG_KEYLIGHT) { - rgb_matrix_set_color(i, RGB_RED); - } - } - } - return false; -} -``` - -Layer indicator on all keys: -```c -bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) { - for (uint8_t i = led_min; i < led_max; i++) { - switch(get_highest_layer(layer_state|default_layer_state)) { - case 2: - rgb_matrix_set_color(i, RGB_BLUE); - break; - case 1: - rgb_matrix_set_color(i, RGB_YELLOW); - break; - default: - break; - } - } - return false; -} -``` - -Layer indicator only on keys with configured keycodes: -```c -bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) { - if (get_highest_layer(layer_state) > 0) { - uint8_t layer = get_highest_layer(layer_state); - - for (uint8_t row = 0; row < MATRIX_ROWS; ++row) { - for (uint8_t col = 0; col < MATRIX_COLS; ++col) { - uint8_t index = g_led_config.matrix_co[row][col]; - - if (index >= led_min && index < led_max && index != NO_LED && - keymap_key_to_keycode(layer, (keypos_t){col,row}) > KC_TRNS) { - rgb_matrix_set_color(index, RGB_GREEN); - } - } - } - } - return false; -} -``` - -::: tip -Split keyboards will require layer state data syncing with `#define SPLIT_LAYER_STATE_ENABLE`. See [Data Sync Options](feature_split_keyboard#data-sync-options) for more details. -::: - -#### Examples {#indicator-examples-2} - -This example sets the modifiers to be a specific color based on the layer state. You can use a switch case here, instead, if you would like. This uses HSV and then converts to RGB, because this allows the brightness to be limited (important when using the WS2812 driver). - -```c -bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) { - HSV hsv = {0, 255, 255}; - - if (layer_state_is(layer_state, 2)) { - hsv = {130, 255, 255}; - } else { - hsv = {30, 255, 255}; - } - - if (hsv.v > rgb_matrix_get_val()) { - hsv.v = rgb_matrix_get_val(); - } - RGB rgb = hsv_to_rgb(hsv); - - for (uint8_t i = led_min; i < led_max; i++) { - if (HAS_FLAGS(g_led_config.flags[i], 0x01)) { // 0x01 == LED_FLAG_MODIFIER - rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); - } - } - return false; -} -``` - -If you want to indicate a Host LED status (caps lock, num lock, etc), you can use something like this to light up the caps lock key: - -```c -bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) { - if (host_keyboard_led_state().caps_lock) { - RGB_MATRIX_INDICATOR_SET_COLOR(5, 255, 255, 255); // assuming caps lock is at led #5 - } else { - RGB_MATRIX_INDICATOR_SET_COLOR(5, 0, 0, 0); - } - return false; -} -``` - -::: tip -RGB indicators on split keyboards will require state information synced to the slave half (e.g. `#define SPLIT_LAYER_STATE_ENABLE`). See [data sync options](feature_split_keyboard#data-sync-options) for more details. -::: - -#### Indicators without RGB Matrix Effect - -If you want to just use RGB indicators without RGB matrix effect, it is not possible to disable the latter because toggling RGB off will disable everything. You can workaround it with solid effect and colors off using this init function: -```c -void keyboard_post_init_user(void) { - rgb_matrix_mode_noeeprom(RGB_MATRIX_SOLID_COLOR); - rgb_matrix_sethsv_noeeprom(HSV_OFF); -} -``` diff --git a/docs/feature_rgblight.md b/docs/feature_rgblight.md deleted file mode 100644 index 682d8b8cba..0000000000 --- a/docs/feature_rgblight.md +++ /dev/null @@ -1,581 +0,0 @@ -# RGB Lighting - -QMK has the ability to control RGB LEDs attached to your keyboard. This is commonly called *underglow*, due to the LEDs often being mounted on the bottom of the keyboard, producing a nice diffused effect when combined with a translucent case. - -![Planck with RGB Underglow](https://raw.githubusercontent.com/qmk/qmk_firmware/3774a7fcdab5544fc787f4c200be05fcd417e31f/keyboards/planck/keymaps/yang/planck-with-rgb-underglow.jpg) - -Some keyboards come with RGB LEDs preinstalled. Others must have them installed after the fact. See the [Hardware Modification](#hardware-modification) section for information on adding RGB lighting to your keyboard. - -Currently QMK supports the following addressable LEDs: - - * WS2811, WS2812, WS2812B, WS2812C, etc. - * SK6812, SK6812MINI, SK6805 - * APA102 - -These LEDs are called "addressable" because instead of using a wire per color, each LED contains a small microchip that understands a special protocol sent over a single wire. The chip passes on the remaining data to the next LED, allowing them to be chained together. In this way, you can easily control the color of the individual LEDs. - -## Usage - -On keyboards with onboard RGB LEDs, it is usually enabled by default. If it is not working for you, check that your `rules.mk` includes the following: - -```make -RGBLIGHT_ENABLE = yes -``` - -::: tip -There are additional configuration options for ARM controllers that offer increased performance over the default WS2812 bitbang driver. Please see [WS2812 Driver](ws2812_driver) for more information. -::: - -For APA102 LEDs, add the following to your `rules.mk`: - -```make -RGBLIGHT_ENABLE = yes -RGBLIGHT_DRIVER = apa102 -``` - -At minimum you must define the data pin your LED strip is connected to, and the number of LEDs in the strip, in your `config.h`. For APA102 LEDs, you must also define the clock pin. If your keyboard has onboard RGB LEDs, and you are simply creating a keymap, you usually won't need to modify these. - -|Define |Description | -|--------------------|-------------------------------------------------------------------------| -|`WS2812_DI_PIN` |The pin connected to the data pin of the LEDs (WS2812) | -|`APA102_DI_PIN` |The pin connected to the data pin of the LEDs (APA102) | -|`APA102_CI_PIN` |The pin connected to the clock pin of the LEDs (APA102) | -|`RGBLIGHT_LED_COUNT`|The number of LEDs connected | -|`RGBLED_SPLIT` |(Optional) For split keyboards, the number of LEDs connected on each half| - -Then you should be able to use the keycodes below to change the RGB lighting to your liking. - -### Color Selection - -QMK uses [Hue, Saturation, and Value](https://en.wikipedia.org/wiki/HSL_and_HSV) to select colors rather than RGB. The color wheel below demonstrates how this works. - -HSV Color Wheel - -Changing the **Hue** cycles around the circle.
-Changing the **Saturation** moves between the inner and outer sections of the wheel, affecting the intensity of the color.
-Changing the **Value** sets the overall brightness.
- -![QMK Color Wheel with HSV Values](https://i.imgur.com/vkYVo66.jpg) - -## Keycodes - -|Key |Aliases |Description | -|-------------------|----------|--------------------------------------------------------------------| -|`RGB_TOG` | |Toggle RGB lighting on or off | -|`RGB_MODE_FORWARD` |`RGB_MOD` |Cycle through modes, reverse direction when Shift is held | -|`RGB_MODE_REVERSE` |`RGB_RMOD`|Cycle through modes in reverse, forward direction when Shift is held| -|`RGB_HUI` | |Increase hue, decrease hue when Shift is held | -|`RGB_HUD` | |Decrease hue, increase hue when Shift is held | -|`RGB_SAI` | |Increase saturation, decrease saturation when Shift is held | -|`RGB_SAD` | |Decrease saturation, increase saturation when Shift is held | -|`RGB_VAI` | |Increase value (brightness), decrease value when Shift is held | -|`RGB_VAD` | |Decrease value (brightness), increase value when Shift is held | -|`RGB_MODE_PLAIN` |`RGB_M_P `|Static (no animation) mode | -|`RGB_MODE_BREATHE` |`RGB_M_B` |Breathing animation mode | -|`RGB_MODE_RAINBOW` |`RGB_M_R` |Rainbow animation mode | -|`RGB_MODE_SWIRL` |`RGB_M_SW`|Swirl animation mode | -|`RGB_MODE_SNAKE` |`RGB_M_SN`|Snake animation mode | -|`RGB_MODE_KNIGHT` |`RGB_M_K` |"Knight Rider" animation mode | -|`RGB_MODE_XMAS` |`RGB_M_X` |Christmas animation mode | -|`RGB_MODE_GRADIENT`|`RGB_M_G` |Static gradient animation mode | -|`RGB_MODE_RGBTEST` |`RGB_M_T` |Red, Green, Blue test animation mode | -|`RGB_MODE_TWINKLE` |`RGB_M_TW`|Twinkle animation mode | - -::: tip -`RGB_*` keycodes cannot be used with functions like `tap_code16(RGB_HUI)` as they're not USB HID keycodes. If you wish to replicate similar behaviour in custom code within your firmware (e.g. inside `encoder_update_user()` or `process_record_user()`), the equivalent [RGB functions](#functions) should be used instead. -::: - - -::: warning -By default, if you have both the RGB Light and the [RGB Matrix](feature_rgb_matrix) feature enabled, these keycodes will work for both features, at the same time. You can disable the keycode functionality by defining the `*_DISABLE_KEYCODES` option for the specific feature. -::: - -## Configuration - -Your RGB lighting can be configured by placing these `#define`s in your `config.h`: - -|Define |Default |Description | -|---------------------------|----------------------------|---------------------------------------------------------------------------------------------------------------------------| -|`RGBLIGHT_HUE_STEP` |`8` |The number of steps to cycle through the hue by | -|`RGBLIGHT_SAT_STEP` |`17` |The number of steps to increment the saturation by | -|`RGBLIGHT_VAL_STEP` |`17` |The number of steps to increment the brightness by | -|`RGBLIGHT_LIMIT_VAL` |`255` |The maximum brightness level | -|`RGBLIGHT_SLEEP` |*Not defined* |If defined, the RGB lighting will be switched off when the host goes to sleep | -|`RGBLIGHT_SPLIT` |*Not defined* |If defined, synchronization functionality for split keyboards is added | -|`RGBLIGHT_DISABLE_KEYCODES`|*Not defined* |If defined, disables the ability to control RGB Light from the keycodes. You must use code functions to control the feature| -|`RGBLIGHT_DEFAULT_MODE` |`RGBLIGHT_MODE_STATIC_LIGHT`|The default mode to use upon clearing the EEPROM | -|`RGBLIGHT_DEFAULT_HUE` |`0` (red) |The default hue to use upon clearing the EEPROM | -|`RGBLIGHT_DEFAULT_SAT` |`UINT8_MAX` (255) |The default saturation to use upon clearing the EEPROM | -|`RGBLIGHT_DEFAULT_VAL` |`RGBLIGHT_LIMIT_VAL` |The default value (brightness) to use upon clearing the EEPROM | -|`RGBLIGHT_DEFAULT_SPD` |`0` |The default speed to use upon clearing the EEPROM | -|`RGBLIGHT_DEFAULT_ON` |`true` |Enable RGB lighting upon clearing the EEPROM | - -## Effects and Animations - -Not only can this lighting be whatever color you want, -if `RGBLIGHT_EFFECT_xxxx` is defined, you also have a number of animation modes at your disposal: - -|Mode number symbol |Additional number |Description | -|-----------------------------|-------------------|---------------------------------------| -|`RGBLIGHT_MODE_STATIC_LIGHT` | *None* |Solid color (this mode is always enabled) | -|`RGBLIGHT_MODE_BREATHING` | 0,1,2,3 |Solid color breathing | -|`RGBLIGHT_MODE_RAINBOW_MOOD` | 0,1,2 |Cycling rainbow | -|`RGBLIGHT_MODE_RAINBOW_SWIRL`| 0,1,2,3,4,5 |Swirling rainbow | -|`RGBLIGHT_MODE_SNAKE` | 0,1,2,3,4,5 |Snake | -|`RGBLIGHT_MODE_KNIGHT` | 0,1,2 |Knight | -|`RGBLIGHT_MODE_CHRISTMAS` | *None* |Christmas | -|`RGBLIGHT_MODE_STATIC_GRADIENT`| 0,1,..,9 |Static gradient | -|`RGBLIGHT_MODE_RGB_TEST` | *None* |RGB Test | -|`RGBLIGHT_MODE_ALTERNATING` | *None* |Alternating | -|`RGBLIGHT_MODE_TWINKLE` | 0,1,2,3,4,5 |Twinkle | - -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/rgblight.h` there is a contrast table between the old mode number and the current symbol. - - -### Effect and Animation Toggles - -Use these defines to add or remove animations from the firmware. When you are running low on flash space, it can be helpful to disable animations you are not using. - -|Define |Default |Description | -|------------------------------------|-------------|-------------------------------------------------------------------------| -|`RGBLIGHT_ANIMATIONS` |*Not defined*|Enable all additional animation modes. (deprecated) | -|`RGBLIGHT_EFFECT_ALTERNATING` |*Not defined*|Enable alternating animation mode. | -|`RGBLIGHT_EFFECT_BREATHING` |*Not defined*|Enable breathing animation mode. | -|`RGBLIGHT_EFFECT_CHRISTMAS` |*Not defined*|Enable christmas animation mode. | -|`RGBLIGHT_EFFECT_KNIGHT` |*Not defined*|Enable knight animation mode. | -|`RGBLIGHT_EFFECT_RAINBOW_MOOD` |*Not defined*|Enable rainbow mood animation mode. | -|`RGBLIGHT_EFFECT_RAINBOW_SWIRL` |*Not defined*|Enable rainbow swirl animation mode. | -|`RGBLIGHT_EFFECT_RGB_TEST` |*Not defined*|Enable RGB test animation mode. | -|`RGBLIGHT_EFFECT_SNAKE` |*Not defined*|Enable snake animation mode. | -|`RGBLIGHT_EFFECT_STATIC_GRADIENT` |*Not defined*|Enable static gradient mode. | -|`RGBLIGHT_EFFECT_TWINKLE` |*Not defined*|Enable twinkle animation mode. | - -::: warning -`RGBLIGHT_ANIMATIONS` is being deprecated and animation modes should be explicitly defined. -::: - -### Effect and Animation Settings - -The following options are used to tweak the various animations: - -|Define |Default |Description | -|------------------------------------|--------------------|-----------------------------------------------------------------------------------------------| -|`RGBLIGHT_EFFECT_BREATHE_CENTER` |*Not defined* |If defined, used to calculate the curve for the breathing animation. Valid values are 1.0 to 2.7 | -|`RGBLIGHT_EFFECT_BREATHE_MAX` |`255` |The maximum brightness for the breathing mode. Valid values are 1 to 255 | -|`RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL`|`40` |How long (in milliseconds) to wait between animation steps for the "Christmas" animation | -|`RGBLIGHT_EFFECT_CHRISTMAS_STEP` |`2` |The number of LEDs to group the red/green colors by for the "Christmas" animation | -|`RGBLIGHT_EFFECT_KNIGHT_LED_NUM` |`RGBLIGHT_LED_COUNT`|The number of LEDs to have the "Knight" animation travel | -|`RGBLIGHT_EFFECT_KNIGHT_LENGTH` |`3` |The number of LEDs to light up for the "Knight" animation | -|`RGBLIGHT_EFFECT_KNIGHT_OFFSET` |`0` |The number of LEDs to start the "Knight" animation from the start of the strip by | -|`RGBLIGHT_RAINBOW_SWIRL_RANGE` |`255` |Range adjustment for the rainbow swirl effect to get different swirls | -|`RGBLIGHT_EFFECT_SNAKE_LENGTH` |`4` |The number of LEDs to light up for the "Snake" animation | -|`RGBLIGHT_EFFECT_TWINKLE_LIFE` |`200` |Adjusts how quickly each LED brightens and dims when twinkling (in animation steps) | -|`RGBLIGHT_EFFECT_TWINKLE_PROBABILITY`|`1/127` |Adjusts how likely each LED is to twinkle (on each animation step) | - -### Example Usage to Reduce Memory Footprint - 1. Use `#undef` to selectively disable animations. The following would disable two animations and save about 4KiB: - -```diff - #undef RGBLIGHT_LED_COUNT -+#undef RGBLIGHT_EFFECT_STATIC_GRADIENT -+#undef RGBLIGHT_EFFECT_RAINBOW_SWIRL - #define RGBLIGHT_LED_COUNT 12 - #define RGBLIGHT_HUE_STEP 8 - #define RGBLIGHT_SAT_STEP 8 -``` - -### Animation Speed - -You can also modify the speeds that the different modes animate at: - -Here is a quick demo on Youtube (with NPKC KC60) (https://www.youtube.com/watch?v=VKrpPAHlisY). - -```c -// How long (in milliseconds) to wait between animation steps for each of the "Solid color breathing" animations -const uint8_t RGBLED_BREATHING_INTERVALS[] PROGMEM = {30, 20, 10, 5}; - -// How long (in milliseconds) to wait between animation steps for each of the "Cycling rainbow" animations -const uint8_t RGBLED_RAINBOW_MOOD_INTERVALS[] PROGMEM = {120, 60, 30}; - -// How long (in milliseconds) to wait between animation steps for each of the "Swirling rainbow" animations -const uint8_t RGBLED_RAINBOW_SWIRL_INTERVALS[] PROGMEM = {100, 50, 20}; - -// How long (in milliseconds) to wait between animation steps for each of the "Snake" animations -const uint8_t RGBLED_SNAKE_INTERVALS[] PROGMEM = {100, 50, 20}; - -// How long (in milliseconds) to wait between animation steps for each of the "Knight" animations -const uint8_t RGBLED_KNIGHT_INTERVALS[] PROGMEM = {127, 63, 31}; - -// How long (in milliseconds) to wait between animation steps for each of the "Twinkle" animations -const uint8_t RGBLED_TWINKLE_INTERVALS[] PROGMEM = {50, 25, 10}; - -// These control which hues are selected for each of the "Static gradient" modes -const uint8_t RGBLED_GRADIENT_RANGES[] PROGMEM = {255, 170, 127, 85, 64}; -``` - -## Lighting Layers - -::: tip -**Note:** Lighting Layers is an RGB Light feature, it will not work for RGB Matrix. See [RGB Matrix Indicators](feature_rgb_matrix#indicators) for details on how to do so. -::: - -By including `#define RGBLIGHT_LAYERS` in your `config.h` file you can enable lighting layers. These make -it easy to use your underglow LEDs as status indicators to show which keyboard layer is currently active, or the state of caps lock, all without disrupting any animations. [Here's a video](https://youtu.be/uLGE1epbmdY) showing an example of what you can do. - -### Defining Lighting Layers {#defining-lighting-layers} - -By default, 8 layers are possible. This can be expanded to as many as 32 by overriding the definition of `RGBLIGHT_MAX_LAYERS` in `config.h` (e.g. `#define RGBLIGHT_MAX_LAYERS 32`). Please note, if you use a split keyboard, you will need to flash both sides of the split after changing this. Also, increasing the maximum will increase the firmware size, and will slow sync on split keyboards. - -To define a layer, we modify `keymap.c` to list the LED ranges and the colors we want to overlay on them using an array of `rgblight_segment_t` using the `RGBLIGHT_LAYER_SEGMENTS` macro. We can define multiple layers and enable/disable them independently: - -```c -// Light LEDs 6 to 9 and 12 to 15 red when caps lock is active. Hard to ignore! -const rgblight_segment_t PROGMEM my_capslock_layer[] = RGBLIGHT_LAYER_SEGMENTS( - {6, 4, HSV_RED}, // Light 4 LEDs, starting with LED 6 - {12, 4, HSV_RED} // Light 4 LEDs, starting with LED 12 -); -// Light LEDs 9 & 10 in cyan when keyboard layer 1 is active -const rgblight_segment_t PROGMEM my_layer1_layer[] = RGBLIGHT_LAYER_SEGMENTS( - {9, 2, HSV_CYAN} -); -// Light LEDs 11 & 12 in purple when keyboard layer 2 is active -const rgblight_segment_t PROGMEM my_layer2_layer[] = RGBLIGHT_LAYER_SEGMENTS( - {11, 2, HSV_PURPLE} -); -// Light LEDs 13 & 14 in green when keyboard layer 3 is active -const rgblight_segment_t PROGMEM my_layer3_layer[] = RGBLIGHT_LAYER_SEGMENTS( - {13, 2, HSV_GREEN} -); -// etc.. -``` - -We combine these layers into an array using the `RGBLIGHT_LAYERS_LIST` macro, and assign it to the `rgblight_layers` variable during keyboard setup. Note that you can only define up to 8 lighting layers. Any extra layers will be ignored. Since the different lighting layers overlap, the order matters in the array, with later layers taking precedence: - -```c -// Now define the array of layers. Later layers take precedence -const rgblight_segment_t* const PROGMEM my_rgb_layers[] = RGBLIGHT_LAYERS_LIST( - my_capslock_layer, - my_layer1_layer, // Overrides caps lock layer - my_layer2_layer, // Overrides other layers - my_layer3_layer // Overrides other layers -); - -void keyboard_post_init_user(void) { - // Enable the LED layers - rgblight_layers = my_rgb_layers; -} -``` -Note: For split keyboards with two controllers, both sides need to be flashed when updating the contents of rgblight_layers. - -### Enabling and disabling lighting layers {#enabling-lighting-layers} - -Everything above just configured the definition of each lighting layer. -We can now enable and disable the lighting layers whenever the state of the keyboard changes: - -```c -bool led_update_user(led_t led_state) { - rgblight_set_layer_state(0, led_state.caps_lock); - return true; -} - -layer_state_t default_layer_state_set_user(layer_state_t state) { - rgblight_set_layer_state(1, layer_state_cmp(state, _DVORAK)); - return state; -} - -layer_state_t layer_state_set_user(layer_state_t state) { - rgblight_set_layer_state(2, layer_state_cmp(state, _FN)); - rgblight_set_layer_state(3, layer_state_cmp(state, _ADJUST)); - return state; -} -``` - -### Lighting layer blink {#lighting-layer-blink} - -By including `#define RGBLIGHT_LAYER_BLINK` in your `config.h` file you can turn a lighting -layer on for a specified duration. Once the specified number of milliseconds has elapsed -the layer will be turned off. This is useful, e.g., if you want to acknowledge some -action (e.g. toggling some setting): - -```c -const rgblight_segment_t PROGMEM _yes_layer[] = RGBLIGHT_LAYER_SEGMENTS( {9, 6, HSV_GREEN} ); -const rgblight_segment_t PROGMEM _no_layer[] = RGBLIGHT_LAYER_SEGMENTS( {9, 6, HSV_RED} ); - -const rgblight_segment_t* const PROGMEM _rgb_layers[] = - RGBLIGHT_LAYERS_LIST( _yes_layer, _no_layer ); - -void keyboard_post_init_user(void) { - rgblight_layers = _rgb_layers; -} - -// Note we user post_process_record_user because we want the state -// after the flag has been flipped... -void post_process_record_user(uint16_t keycode, keyrecord_t *record) { - switch (keycode) { - case QK_DEBUG_TOGGLE: - rgblight_blink_layer(debug_enable ? 0 : 1, 500); - break; - - case NK_TOGG: - case NK_ON: - case NK_OFF: - rgblight_blink_layer(keymap_config.nkro ? 0 : 1, 500); - break; - } -} -``` - -You can also use `rgblight_blink_layer_repeat` to specify the amount of times the layer is supposed to blink. Using the layers from above, -```c -void post_process_record_user(uint16_t keycode, keyrecord_t *record) { - switch (keycode) { - case QK_DEBUG_TOGGLE: - rgblight_blink_layer_repeat(debug_enable ? 0 : 1, 200, 3); - break; - } -} -``` -would turn the layer 0 (or 1) on and off again three times when `DB_TOGG` is pressed. - -Blinking accumulates layers so if multiple layers are set blinking at the same time they will all blink for the duration and repeat times of the last layer to be blinked. -To stop these other layers from blinking use `rgblight_unblink_layer` or `rgblight_unblink_all_but_layer`: - -```c -rgblight_blink_layer(1, 500); -rgblight_unblink_all_but_layer(1); -``` - -```c -rgblight_unblink_layer(3); -rgblight_blink_layer(2, 500); -``` - -::: warning -Lighting layers on split keyboards will require layer state synced to the slave half (e.g. `#define SPLIT_LAYER_STATE_ENABLE`). See [data sync options](feature_split_keyboard#data-sync-options) for more details. -::: - -### Overriding RGB Lighting on/off status - -Normally lighting layers are not shown when RGB Lighting is disabled (e.g. with `RGB_TOG` keycode). If you would like lighting layers to work even when the RGB Lighting is otherwise off, add `#define RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF` to your `config.h`. - -### Retain brightness - -Usually lighting layers apply their configured brightness once activated. If you would like lighting layers to retain the currently used brightness (as returned by `rgblight_get_val()`), add `#define RGBLIGHT_LAYERS_RETAIN_VAL` to your `config.h`. - -## 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/rgblight.h) for the full list, but the most commonly used functions include: - -### Low level Functions -|Function |Description | -|--------------------------------------------|-------------------------------------------| -|`rgblight_set()` |Flush out led buffers to LEDs | -|`rgblight_set_clipping_range(pos, num)` |Set clipping Range. see [Clipping Range](#clipping-range) | - -### Effects and Animations Functions -#### effect range setting -|Function |Description | -|--------------------------------------------|------------------| -|`rgblight_set_effect_range(pos, num)` |Set Effects Range | - -#### direct operation -|Function |Description | -|--------------------------------------------|-------------| -|`rgblight_setrgb_at(r, g, b, index)` |Set a single LED to the given RGB value, where `r`/`g`/`b` are between 0 and 255 and `index` is between 0 and `RGBLIGHT_LED_COUNT` (not written to EEPROM) | -|`rgblight_sethsv_at(h, s, v, index)` |Set a single LED to the given HSV value, where `h`/`s`/`v` are between 0 and 255, and `index` is between 0 and `RGBLIGHT_LED_COUNT` (not written to EEPROM) | -|`rgblight_setrgb_range(r, g, b, start, end)`|Set a continuous range of LEDs to the given RGB value, where `r`/`g`/`b` are between 0 and 255 and `start`(included) and `stop`(excluded) are between 0 and `RGBLIGHT_LED_COUNT` (not written to EEPROM)| -|`rgblight_sethsv_range(h, s, v, start, end)`|Set a continuous range of LEDs to the given HSV value, where `h`/`s`/`v` are between 0 and 255, and `start`(included) and `stop`(excluded) are between 0 and `RGBLIGHT_LED_COUNT` (not written to EEPROM)| -|`rgblight_setrgb(r, g, b)` |Set effect range LEDs to the given RGB value where `r`/`g`/`b` are between 0 and 255 (not written to EEPROM) | -|`rgblight_setrgb_master(r, g, b)` |Set the LEDs on the master side to the given RGB value, where `r`/`g`/`b` are between 0 and 255 (not written to EEPROM) | -|`rgblight_setrgb_slave(r, g, b)` |Set the LEDs on the slave side to the given RGB value, where `r`/`g`/`b` are between 0 and 255 (not written to EEPROM) | -|`rgblight_sethsv_master(h, s, v)` |Set the LEDs on the master side to the given HSV value, where `h`/`s`/`v` are between 0 and 255 (not written to EEPROM) | -|`rgblight_sethsv_slave(h, s, v)` |Set the LEDs on the slave side to the given HSV value, where `h`/`s`/`v` are between 0 and 255 (not written to EEPROM) | - -Example: -```c -rgblight_sethsv_at(HSV_WHITE, 0); // led 0 -rgblight_sethsv_at(HSV_RED, 1); // led 1 -rgblight_sethsv_at(HSV_GREEN, 2); // led 2 -// The above functions automatically calls rgblight_set(), so there is no need to call it explicitly. -// Note that it is inefficient to call repeatedly. -``` - -#### effect mode change -|Function |Description | -|--------------------------------------------|-------------| -|`rgblight_mode(x)` |Set the mode, if RGB animations are enabled | -|`rgblight_mode_noeeprom(x)` |Set the mode, if RGB animations are enabled (not written to EEPROM) | -|`rgblight_step()` |Change the mode to the next RGB animation in the list of enabled RGB animations | -|`rgblight_step_noeeprom()` |Change the mode to the next RGB animation in the list of enabled RGB animations (not written to EEPROM) | -|`rgblight_step_reverse()` |Change the mode to the previous RGB animation in the list of enabled RGB animations | -|`rgblight_step_reverse_noeeprom()` |Change the mode to the previous RGB animation in the list of enabled RGB animations (not written to EEPROM) | -|`rgblight_reload_from_eeprom()` |Reload the effect configuration (enabled, mode and color) from EEPROM | - -#### effects mode disable/enable -|Function |Description | -|--------------------------------------------|-------------| -|`rgblight_toggle()` |Toggle effect range LEDs between on and off | -|`rgblight_toggle_noeeprom()` |Toggle effect range LEDs between on and off (not written to EEPROM) | -|`rgblight_enable()` |Turn effect range LEDs on, based on their previous state | -|`rgblight_enable_noeeprom()` |Turn effect range LEDs on, based on their previous state (not written to EEPROM) | -|`rgblight_disable()` |Turn effect range LEDs off | -|`rgblight_disable_noeeprom()` |Turn effect range LEDs off (not written to EEPROM) | - -#### hue, sat, val change -|Function |Description | -|--------------------------------------------|-------------| -|`rgblight_increase_hue()` |Increase the hue for effect range LEDs. This wraps around at maximum hue | -|`rgblight_increase_hue_noeeprom()` |Increase the hue for effect range LEDs. This wraps around at maximum hue (not written to EEPROM) | -|`rgblight_decrease_hue()` |Decrease the hue for effect range LEDs. This wraps around at minimum hue | -|`rgblight_decrease_hue_noeeprom()` |Decrease the hue for effect range LEDs. This wraps around at minimum hue (not written to EEPROM) | -|`rgblight_increase_sat()` |Increase the saturation for effect range LEDs. This stops at maximum saturation | -|`rgblight_increase_sat_noeeprom()` |Increase the saturation for effect range LEDs. This stops at maximum saturation (not written to EEPROM) | -|`rgblight_decrease_sat()` |Decrease the saturation for effect range LEDs. This stops at minimum saturation | -|`rgblight_decrease_sat_noeeprom()` |Decrease the saturation for effect range LEDs. This stops at minimum saturation (not written to EEPROM) | -|`rgblight_increase_val()` |Increase the value for effect range LEDs. This stops at maximum value | -|`rgblight_increase_val_noeeprom()` |Increase the value for effect range LEDs. This stops at maximum value (not written to EEPROM) | -|`rgblight_decrease_val()` |Decrease the value for effect range LEDs. This stops at minimum value | -|`rgblight_decrease_val_noeeprom()` |Decrease the value for effect range LEDs. This stops at minimum value (not written to EEPROM) | -|`rgblight_sethsv(h, s, v)` |Set effect range LEDs to the given HSV value where `h`/`s`/`v` are between 0 and 255 | -|`rgblight_sethsv_noeeprom(h, s, v)` |Set effect range LEDs to the given HSV value where `h`/`s`/`v` are between 0 and 255 (not written to EEPROM) | - -#### Speed functions -|Function |Description | -|--------------------------------------------|-------------| -|`rgblight_increase_speed()` |Increases the animation speed | -|`rgblight_increase_speed_noeeprom()` |Increases the animation speed (not written to EEPROM) | -|`rgblight_decrease_speed()` |Decreases the animation speed | -|`rgblight_decrease_speed_noeeprom()` |Decreases the animation speed (not written to EEPROM) | -|`rgblight_set_speed()` |Sets the speed. Value is between 0 and 255 | -|`rgblight_set_speed_noeeprom()` |Sets the speed. Value is between 0 and 255 (not written to EEPROM) | - - -#### layer functions -|Function |Description | -|--------------------------------------------|-------------| -|`rgblight_get_layer_state(i)` |Returns `true` if lighting layer `i` is enabled | -|`rgblight_set_layer_state(i, is_on)` |Enable or disable lighting layer `i` based on value of `bool is_on` | - -#### query -|Function |Description | -|-----------------------|---------------------------| -|`rgblight_is_enabled()`|Gets current on/off status | -|`rgblight_get_mode()` |Gets current mode | -|`rgblight_get_hue()` |Gets current hue | -|`rgblight_get_sat()` |Gets current sat | -|`rgblight_get_val()` |Gets current val | -|`rgblight_get_speed()` |Gets current speed | - -## Colors - -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_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); -rgblight_sethsv_noeeprom(HSV_GREEN); -rgblight_setrgb_at(RGB_GOLD, 3); -rgblight_sethsv_range(HSV_WHITE, 0, 6); -``` - -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 - -If you want to make the logical order of LEDs different from the electrical connection order, you can do this by defining the `RGBLIGHT_LED_MAP` macro in your `config.h`. - -Normally, the contents of the LED buffer are output to the LEDs in the same order. -simple dicrect - -By defining `RGBLIGHT_LED_MAP` as in the example below, you can specify the LED with addressing in reverse order of the electrical connection order. - -```c -// config.h - -#define RGBLIGHT_LED_COUNT 4 -#define RGBLIGHT_LED_MAP { 3, 2, 1, 0 } - -``` -simple mapped - -## Clipping Range - -Using the `rgblight_set_clipping_range()` function, you can prepare more buffers than the actual number of LEDs, and output some of the buffers to the LEDs. This is useful if you want the split keyboard to treat left and right LEDs as logically contiguous. - -You can set the Clipping Range by executing the following code. - -```c -// some source -rgblight_set_clipping_range(3, 4); -``` -clip direct - -In addition to setting the Clipping Range, you can use `RGBLIGHT_LED_MAP` together. - -```c -// config.h -#define RGBLIGHT_LED_COUNT 8 -#define RGBLIGHT_LED_MAP { 7, 6, 5, 4, 3, 2, 1, 0 } - -// some source -rgblight_set_clipping_range(3, 4); -``` -clip mapped - -## Hardware Modification - -If your keyboard lacks onboard underglow LEDs, you may often be able to solder on an RGB LED strip yourself. You will need to find an unused pin to wire to the data pin of your LED strip. Some keyboards may break out unused pins from the MCU to make soldering easier. The other two pins, VCC and GND, must also be connected to the appropriate power pins. - -## Velocikey - -Velocikey is a feature that lets you control the speed of lighting effects (like the Rainbow Swirl effect) with the speed of your typing. The faster you type, the faster the lights will go! - -### Usage -For Velocikey to take effect, there are two steps. First, when compiling your keyboard, you'll need to set `VELOCIKEY_ENABLE=yes` in `rules.mk`, e.g.: - -``` -MOUSEKEY_ENABLE = no -STENO_ENABLE = no -EXTRAKEY_ENABLE = yes -VELOCIKEY_ENABLE = yes -``` - -Then, while using your keyboard, you need to also turn it on with the `VK_TOGG` keycode, which toggles the feature on and off. - -The following light effects will all be controlled by Velocikey when it is enabled: - - RGB Breathing - - RGB Rainbow Mood - - RGB Rainbow Swirl - - RGB Snake - - RGB Knight - -Support for LED breathing effects is planned but not available yet. - - As long as Velocikey is enabled, it will control the speed regardless of any other speed setting that your RGB lights are currently on. - - ### Configuration - Velocikey doesn't currently support any configuration via keyboard settings. If you want to adjust something like the speed increase or decay rate, you would need to edit `velocikey.c` and adjust the values there to achieve the kinds of speeds that you like. diff --git a/docs/feature_secure.md b/docs/feature_secure.md deleted file mode 100644 index 5ca9eed65f..0000000000 --- a/docs/feature_secure.md +++ /dev/null @@ -1,56 +0,0 @@ -# Secure - -The secure feature aims to prevent unwanted interaction without user intervention. - -::: tip -Secure does **not** currently implement encryption/decryption/etc and should not be a replacement where a strong hardware/software based solution is required. -::: - -### Unlock sequence - -To unlock, the user must perform a set of actions. This can optionally be configured to be multiple keys. - -* While unlocking all keyboard input is ignored -* Incorrect attempts will revert back to the previously locked state - -### Automatic Locking - -Once unlocked, the keyboard will revert back to a locked state after the configured timeout. -The timeout can be refreshed by using the `secure_activity_event` function, for example from one of the various [hooks](custom_quantum_functions). - -## Usage - -Add the following to your `rules.mk`: - -```make -SECURE_ENABLE = yes -``` - -## Keycodes - -| Key |Aliases | Description | -|---------------------|---------|--------------------------------------------------------------------------------| -| `QK_SECURE_LOCK` |`SE_LOCK`| Revert back to a locked state | -| `QK_SECURE_UNLOCK` |`SE_UNLK`| Forces unlock without performing a unlock sequence | -| `QK_SECURE_TOGGLE` |`SE_TOGG`| Toggle directly between locked and unlock without performing a unlock sequence | -| `QK_SECURE_REQUEST` |`SE_REQ` | Request that user perform the unlock sequence | - -## Configuration - -| Define | Default | Description | -|-------------------------|----------------|---------------------------------------------------------------------------------| -|`SECURE_UNLOCK_TIMEOUT` | `5000` | Timeout for the user to perform the configured unlock sequence - `0` to disable | -|`SECURE_IDLE_TIMEOUT` | `60000` | Timeout while unlocked before returning to locked - `0` to disable | -|`SECURE_UNLOCK_SEQUENCE` | `{ { 0, 0 } }` | Array of matrix locations describing a sequential sequence of keypresses | - -## Functions - -| Function | Description | -|---------------------------|----------------------------------------------------------------------------| -| `secure_is_locked()` | Check if the device is currently locked | -| `secure_is_unlocking()` | Check if an unlock sequence is currently in progress | -| `secure_is_unlocked()` | Check if the device is currently unlocked | -| `secure_lock()` | Lock down the device | -| `secure_unlock()` | Force unlock the device - bypasses user unlock sequence | -| `secure_request_unlock()` | Begin listening for an unlock sequence | -| `secure_activity_event()` | Flag that user activity has happened and the device should remain unlocked | diff --git a/docs/feature_send_string.md b/docs/feature_send_string.md deleted file mode 100644 index 97e4ccc809..0000000000 --- a/docs/feature_send_string.md +++ /dev/null @@ -1,226 +0,0 @@ -# Send String {#send-string} - -The Send String API is part of QMK's macro system. It allows for sequences of keystrokes to be sent automatically. - -The full ASCII character set is supported, along with all of the keycodes in the Basic Keycode range (as these are the only ones that will actually be sent to the host). - -::: tip -Unicode characters are **not** supported with this API -- see the [Unicode](feature_unicode) feature instead. -::: - -## Usage {#usage} - -Send String is enabled by default, so there is usually no need for any special setup. However, if it is disabled, add the following to your `rules.mk`: - -```make -SEND_STRING_ENABLE = yes -``` - -## Basic Configuration {#basic-configuration} - -Add the following to your `config.h`: - -|Define |Default |Description | -|-----------------|----------------|------------------------------------------------------------------------------------------------------------| -|`SENDSTRING_BELL`|*Not defined* |If the [Audio](feature_audio) feature is enabled, the `\a` character (ASCII `BEL`) will beep the speaker.| -|`BELL_SOUND` |`TERMINAL_SOUND`|The song to play when the `\a` character is encountered. By default, this is an eighth note of C5. | - -## Keycodes {#keycodes} - -The Send String functions accept C string literals, but specific keycodes can be injected with the below macros. All of the keycodes in the [Basic Keycode range](keycodes_basic) are supported (as these are the only ones that will actually be sent to the host), but with an `X_` prefix instead of `KC_`. - -|Macro |Description | -|--------------|-------------------------------------------------------------------| -|`SS_TAP(x)` |Send a keydown, then keyup, event for the given Send String keycode| -|`SS_DOWN(x)` |Send a keydown event for the given Send String keycode | -|`SS_UP(x)` |Send a keyup event for the given Send String keycode | -|`SS_DELAY(ms)`|Wait for `ms` milliseconds | - -The following characters are also mapped to their respective keycodes for convenience: - -|Character|Hex |ASCII|Keycode | -|---------|------|-----|--------------| -|`\b` |`\x08`|`BS` |`KC_BACKSPACE`| -|`\e` |`\x09`|`ESC`|`KC_ESCAPE` | -|`\n` |`\x0A`|`LF` |`KC_ENTER` | -|`\t` |`\x1B`|`TAB`|`KC_TAB` | -| |`\x7F`|`DEL`|`KC_DELETE` | - -### Language Support {#language-support} - -By default, Send String assumes your OS keyboard layout is set to US ANSI. If you are using a different keyboard layout, you can [override the lookup tables used to convert ASCII characters to keystrokes](reference_keymap_extras#sendstring-support). - -## Examples {#examples} - -### Hello World {#example-hello-world} - -A simple custom keycode which types out "Hello, world!" and the Enter key when pressed. - -Add the following to your `keymap.c`: - -```c -bool process_record_user(uint16_t keycode, keyrecord_t *record) { - switch (keycode) { - case SS_HELLO: - if (record->event.pressed) { - SEND_STRING("Hello, world!\n"); - } - return false; - } - - return true; -} -``` - -### Keycode Injection {#example-keycode-injection} - -This example types out opening and closing curly braces, then taps the left arrow key to move the cursor between the two. - -```c -SEND_STRING("{}" SS_TAP(X_LEFT)); -``` - -This example types Ctrl+A, then Ctrl+C, without releasing Ctrl. - -```c -SEND_STRING(SS_LCTL("ac")); -``` - -## API {#api} - -### `void send_string(const char *string)` {#api-send-string} - -Type out a string of ASCII characters. - -This function simply calls `send_string_with_delay(string, 0)`. - -#### Arguments {#api-send-string-arguments} - - - `const char *string` - The string to type out. - ---- - -### `void send_string_with_delay(const char *string, uint8_t interval)` {#api-send-string-with-delay} - -Type out a string of ASCII characters, with a delay between each character. - -#### Arguments {#api-send-string-with-delay-arguments} - - - `const char *string` - The string to type out. - - `uint8_t interval` - The amount of time, in milliseconds, to wait before typing the next character. - ---- - -### `void send_string_P(const char *string)` {#api-send-string-p} - -Type out a PROGMEM string of ASCII characters. - -On ARM devices, this function is simply an alias for `send_string_with_delay(string, 0)`. - -#### Arguments {#api-send-string-p-arguments} - - - `const char *string` - The string to type out. - ---- - -### `void send_string_with_delay_P(const char *string, uint8_t interval)` {#api-send-string-with-delay-p} - -Type out a PROGMEM string of ASCII characters, with a delay between each character. - -On ARM devices, this function is simply an alias for `send_string_with_delay(string, interval)`. - -#### Arguments {#api-send-string-with-delay-p-arguments} - - - `const char *string` - The string to type out. - - `uint8_t interval` - The amount of time, in milliseconds, to wait before typing the next character. - ---- - -### `void send_char(char ascii_code)` {#api-send-char} - -Type out an ASCII character. - -#### Arguments {#api-send-char-arguments} - - - `char ascii_code` - The character to type. - ---- - -### `void send_dword(uint32_t number)` {#api-send-dword} - -Type out an eight digit (unsigned 32-bit) hexadecimal value. - -The format is `[0-9a-f]{8}`, eg. `00000000` through `ffffffff`. - -#### Arguments {#api-send-dword-arguments} - - - `uint32_t number` - The value to type, from 0 to 4,294,967,295. - ---- - -### `void send_word(uint16_t number)` {#api-send-word} - -Type out a four digit (unsigned 16-bit) hexadecimal value. - -The format is `[0-9a-f]{4}`, eg. `0000` through `ffff`. - -#### Arguments {#api-send-word-arguments} - - - `uint16_t number` - The value to type, from 0 to 65,535. - ---- - -### `void send_byte(uint8_t number)` {#api-send-bytes} - -Type out a two digit (8-bit) hexadecimal value. - -The format is `[0-9a-f]{2}`, eg. `00` through `ff`. - -#### Arguments {#api-send-byte-arguments} - - - `uint8_t number` - The value to type, from 0 to 255. - ---- - -### `void send_nibble(uint8_t number)` {#api-send-nibble} - -Type out a single hexadecimal digit. - -The format is `[0-9a-f]{1}`, eg. `0` through `f`. - -#### Arguments {#api-send-nibble-arguments} - - - `uint8_t number` - The value to type, from 0 to 15. - ---- - -### `void tap_random_base64(void)` {#api-tap-random-base64} - -Type a pseudorandom character from the set `A-Z`, `a-z`, `0-9`, `+` and `/`. - ---- - -### `SEND_STRING(string)` {#api-send-string-macro} - -Shortcut macro for `send_string_with_delay_P(PSTR(string), 0)`. - -On ARM devices, this define evaluates to `send_string_with_delay(string, 0)`. - ---- - -### `SEND_STRING_DELAY(string, interval)` {#api-send-string-delay-macro} - -Shortcut macro for `send_string_with_delay_P(PSTR(string), interval)`. - -On ARM devices, this define evaluates to `send_string_with_delay(string, interval)`. diff --git a/docs/feature_sequencer.md b/docs/feature_sequencer.md deleted file mode 100644 index f5f1f549af..0000000000 --- a/docs/feature_sequencer.md +++ /dev/null @@ -1,89 +0,0 @@ -# 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)! - -::: warning -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 - -|Key |Aliases |Description | -|-------------------------------|---------|---------------------------------------------------| -|`QK_SEQUENCER_ON` |`SQ_ON` |Start the step sequencer | -|`QK_SEQUENCER_OFF` |`SQ_OFF` |Stop the step sequencer | -|`QK_SEQUENCER_TOGGLE` |`SQ_TOGG`|Toggle the step sequencer playback | -|`QK_SEQUENCER_STEPS_ALL` |`SQ_SALL`|Enable all the steps | -|`QK_SEQUENCER_STEPS_CLEAR` |`SQ_SCLR`|Disable all the steps | -|`QK_SEQUENCER_TEMPO_DOWN` |`SQ_TMPD`|Decrease the tempo | -|`QK_SEQUENCER_TEMPO_UP` |`SQ_TMPU`|Increase the tempo | -|`QK_SEQUENCER_RESOLUTION_DOWN` |`SQ_RESD`|Change to the slower resolution | -|`QK_SEQUENCER_RESOLUTION_UP` |`SQ_RESU`|Change to the faster resolution | -|`SQ_S(n)` | |Toggle the step `n` | -|`SQ_R(n)` | |Set the resolution to n | -|`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/feature_space_cadet.md b/docs/feature_space_cadet.md deleted file mode 100644 index cbb79e10ad..0000000000 --- a/docs/feature_space_cadet.md +++ /dev/null @@ -1,60 +0,0 @@ -# Space Cadet: The Future, Built In - -Steve Losh described the [Space Cadet Shift](https://web.archive.org/web/20230330090938/https://stevelosh.com/blog/2012/10/a-modern-space-cadet/) quite well. Essentially, when you tap Left Shift on its own, you get an opening parenthesis; tap Right Shift on its own and you get the closing one. When held, the Shift keys function as normal. Yes, it's as cool as it sounds, and now even cooler supporting Control and Alt as well! - -## Usage - -Firstly, in your keymap, do one of the following: -- Replace the Left Shift key with `SC_LSPO` (Left Shift, Parenthesis Open), and Right Shift with `SC_RSPC` (Right Shift, Parenthesis Close). -- Replace the Left Control key with `SC_LCPO` (Left Control, Parenthesis Open), and Right Control with `SC_RCPC` (Right Control, Parenthesis Close). -- Replace the Left Alt key with `SC_LAPO` (Left Alt, Parenthesis Open), and Right Alt with `SC_RAPC` (Right Alt, Parenthesis Close). -- Replace any Shift key in your keymap with `SC_SENT` (Right Shift, Enter). - -## Keycodes - -|Keycode |Aliases |Description | -|----------------------------------------------|---------|----------------------------------------| -|`QK_SPACE_CADET_LEFT_CTRL_PARENTHESIS_OPEN` |`SC_LCPO`|Left Control when held, `(` when tapped | -|`QK_SPACE_CADET_RIGHT_CTRL_PARENTHESIS_CLOSE` |`SC_RCPC`|Right Control when held, `)` when tapped| -|`QK_SPACE_CADET_LEFT_SHIFT_PARENTHESIS_OPEN` |`SC_LSPO`|Left Shift when held, `(` when tapped | -|`QK_SPACE_CADET_RIGHT_SHIFT_PARENTHESIS_CLOSE`|`SC_RSPC`|Right Shift when held, `)` when tapped | -|`QK_SPACE_CADET_LEFT_ALT_PARENTHESIS_OPEN` |`SC_LAPO`|Left Alt when held, `(` when tapped | -|`QK_SPACE_CADET_RIGHT_ALT_PARENTHESIS_CLOSE` |`SC_RAPC`|Right Alt when held, `)` when tapped | -|`QK_SPACE_CADET_RIGHT_SHIFT_ENTER` |`SC_SENT`|Right Shift when held, Enter when tapped| - -## Caveats - -Space Cadet's functionality can conflict with the default Command functionality when both Shift keys are held at the same time. See the [Command feature](feature_command) for info on how to change it, or make sure that Command is disabled in your `rules.mk` with: - -```make -COMMAND_ENABLE = no -``` - -## Configuration - -By default Space Cadet assumes a US ANSI layout, but if your layout uses different keys for parentheses, you can redefine them in your `config.h`. In addition, you can redefine the modifier to send on tap, or even send no modifier at all. The new configuration defines bundle all options up into a single define of 3 key codes in this order: the `Modifier` when held or when used with other keys, the `Tap Modifer` sent when tapped (no modifier if `KC_TRNS`), finally the `Keycode` sent when tapped. Now keep in mind, mods from other keys will still apply to the `Keycode` if say `KC_RSFT` is held while tapping `SC_LSPO` key with `KC_TRNS` as the `Tap Modifer`. - -|Define |Default |Description | -|----------------|-------------------------------|---------------------------------------------------------------------------------| -|`LSPO_KEYS` |`KC_LSFT, LSPO_MOD, LSPO_KEY` |Send `KC_LSFT` when held, the mod and key defined by `LSPO_MOD` and `LSPO_KEY`. | -|`RSPC_KEYS` |`KC_RSFT, RSPC_MOD, RSPC_KEY` |Send `KC_RSFT` when held, the mod and key defined by `RSPC_MOD` and `RSPC_KEY`. | -|`LCPO_KEYS` |`KC_LCTL, KC_LSFT, KC_9` |Send `KC_LCTL` when held, the mod `KC_LSFT` with the key `KC_9` when tapped. | -|`RCPC_KEYS` |`KC_RCTL, KC_RSFT, KC_0` |Send `KC_RCTL` when held, the mod `KC_RSFT` with the key `KC_0` when tapped. | -|`LAPO_KEYS` |`KC_LALT, KC_LSFT, KC_9` |Send `KC_LALT` when held, the mod `KC_LSFT` with the key `KC_9` when tapped. | -|`RAPC_KEYS` |`KC_RALT, KC_RSFT, KC_0` |Send `KC_RALT` when held, the mod `KC_RSFT` with the key `KC_0` when tapped. | -|`SFTENT_KEYS` |`KC_RSFT, KC_TRNS, SFTENT_KEY` |Send `KC_RSFT` when held, no mod with the key `SFTENT_KEY` when tapped. | -|`SPACE_CADET_MODIFIER_CARRYOVER` |*Not defined* |Store current modifiers before the hold mod is pressed and use them with the tap mod and keycode. Useful for when you frequently release a modifier before triggering Space Cadet. | - - -## Obsolete Configuration - -These defines are used in the above defines internally to support backwards compatibility, so you may continue to use them, however the above defines open up a larger range of flexibility than before. As an example, say you want to not send any modifier when you tap just `SC_LSPO`, with the old defines you had an all or nothing choice of using the `DISABLE_SPACE_CADET_MODIFIER` define. Now you can define that key as: `#define LSPO_KEYS KC_LSFT, KC_TRNS, KC_9`. This tells the system to set Left Shift if held or used with other keys, then on tap send no modifier (transparent) with the `KC_9`. - -|Define |Default |Description | -|------------------------------|-------------|------------------------------------------------------------------| -|`LSPO_KEY` |`KC_9` |The keycode to send when Left Shift is tapped | -|`RSPC_KEY` |`KC_0` |The keycode to send when Right Shift is tapped | -|`LSPO_MOD` |`KC_LSFT` |The modifier to apply to `LSPO_KEY` | -|`RSPC_MOD` |`KC_RSFT` |The modifier to apply to `RSPC_KEY` | -|`SFTENT_KEY` |`KC_ENT` |The keycode to send when the Shift key is tapped | -|`DISABLE_SPACE_CADET_MODIFIER`|*Not defined*|If defined, prevent the Space Cadet from applying a modifier | diff --git a/docs/feature_split_keyboard.md b/docs/feature_split_keyboard.md deleted file mode 100644 index c39d0a7e08..0000000000 --- a/docs/feature_split_keyboard.md +++ /dev/null @@ -1,508 +0,0 @@ -# Split Keyboard - -Many keyboards in the QMK Firmware repo are "split" keyboards. They use two controllers—one plugging into USB, and the second connected by a serial or an I2C connection over a TRRS or similar cable. - -Split keyboards can have a lot of benefits, but there is some additional work needed to get them enabled. - -QMK Firmware has a generic implementation that is usable by any board, as well as numerous board specific implementations. - -For this, we will mostly be talking about the generic implementation used by the Let's Split and other keyboards. - -::: warning -ARM split supports most QMK subsystems when using the 'serial' and 'serial_usart' drivers. I2C slave is currently unsupported. -::: - -::: warning -Both sides must use the same MCU family, for eg two Pro Micro-compatible controllers or two Blackpills. Currently, mixing AVR and ARM is not possible as ARM vs AVR uses different method for serial communication, and are not compatible. Moreover Blackpill's uses 3.3v logic, and atmega32u4 uses 5v logic. -::: - -## Compatibility Overview - -| Transport | AVR | ARM | -|------------------------------|--------------------|--------------------| -| ['serial'](serial_driver) | :heavy_check_mark: | :white_check_mark: 1 | -| I2C | :heavy_check_mark: | | - -Notes: - -1. Both hardware and software limitations are detailed within the [driver documentation](serial_driver). - -## Hardware Configuration - -This assumes that you're using two Pro Micro-compatible controllers, and are using TRRS jacks to connect to two halves. - -### Required Hardware - -Apart from diodes and key switches for the keyboard matrix in each half, you will need 2x TRRS sockets and 1x TRRS cable. - -Alternatively, you can use any sort of cable and socket that has at least 3 wires. - -If you want to use I2C to communicate between halves, you will need a cable with at least 4 wires and 2x 4.7kΩ pull-up resistors. - -#### Considerations - -The most commonly used connection is a TRRS cable and jacks. These provide 4 wires, making them very useful for split keyboards, and are easy to find. - -However, since one of the wires carries VCC, this means that the boards are not hot pluggable. You should always disconnect the board from USB before unplugging and plugging in TRRS cables, or you can short the controller, or worse. - -Another option is to use phone cables (as in, old school RJ-11/RJ-14 cables). Make sure that you use one that actually supports 4 wires/lanes. - -However, USB cables, SATA cables, and even just 4 wires have been known to be used for communication between the controllers. - -::: warning -Using USB cables for communication between the controllers works just fine, but the connector could be mistaken for a normal USB connection and potentially short out the keyboard, depending on how it's wired. For this reason, they are not recommended for connecting split keyboards. -::: - -### Serial Wiring - -The 3 wires of the TRS/TRRS cable need to connect GND, VCC, and D0/D1/D2/D3 (aka PD0/PD1/PD2/PD3) between the two Pro Micros. - -::: tip -Note that the pin used here is actually set by `SOFT_SERIAL_PIN` below. -::: - -sk-pd0-connection-mono -sk-pd2-connection-mono - -### I2C Wiring - -The 4 wires of the TRRS cable need to connect GND, VCC, and SCL and SDA (aka PD0/pin 3 and PD1/pin 2, respectively) between the two Pro Micros. - -The pull-up resistors may be placed on either half. If you wish to use the halves independently, it is also possible to use 4 resistors and have the pull-ups in both halves. -Note that the total resistance for the connected system should be within spec at 2.2k-10kOhm, with an 'ideal' at 4.7kOhm, regardless of the placement and number. - -sk-i2c-connection-mono - -## Firmware Configuration - -To enable the split keyboard feature, add the following to your `rules.mk`: - -```make -SPLIT_KEYBOARD = yes -``` - -If you're using a custom transport (communication method), then you will also need to add: - -```make -SPLIT_TRANSPORT = custom -``` - -### Layout Macro - -Configuring your layout in a split keyboard works slightly differently to a non-split keyboard. Take for example the following layout. The top left numbers refer to the matrix row and column, and the bottom right are the order of the keys in the layout: - -![Physical layout](https://i.imgur.com/QeY6kMQ.png) - -Since the matrix scanning procedure operates on entire rows, it first populates the left half's rows, then the right half's. Thus, the matrix as QMK views it has double the rows instead of double the columns: - -![Matrix](https://i.imgur.com/4wjJzBU.png) - -### Setting Handedness - -By default, the firmware does not know which side is which; it needs some help to determine that. There are several ways to do this, listed in order of precedence. - -#### Handedness by Pin - -You can configure the firmware to read a pin on the controller to determine handedness. To do this, add the following to your `config.h` file: - -```c -#define SPLIT_HAND_PIN B7 -``` - -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 - -You can configure the firmware to read key matrix pins on the controller to determine handedness. To do this, add the following to your `config.h` file: - -```c -#define SPLIT_HAND_MATRIX_GRID D0, F1 -``` - -The first pin is the output pin and the second is the input pin. - -Some keyboards have unused intersections in the key matrix. This setting uses one of these unused intersections to determine the handedness. - -Normally, when a diode is connected to an intersection, it is judged to be right. If you add the following definition, it will be judged to be left. - -```c -#define SPLIT_HAND_MATRIX_GRID_LOW_IS_LEFT -``` - -Note that adding a diode at a previously unused intersection will effectively tell the firmware that there is a key held down at that point. You can instruct qmk to ignore that intersection by defining `MATRIX_MASKED` and then defining a `matrix_row_t matrix_mask[MATRIX_ROWS]` array in your keyboard config. Each bit of a single value (starting form the least-significant bit) is used to tell qmk whether or not to pay attention to key presses at that intersection. - -While `MATRIX_MASKED` isn't necessary to use `SPLIT_HAND_MATRIX_GRID` successfully, without it you may experience issues trying to suspend your computer with your keyboard attached as the matrix will always report at least one key-press. - -#### Handedness by EEPROM - -This method sets the keyboard's handedness by setting a flag in the persistent storage (`EEPROM`). This is checked when the controller first starts up, and determines what half the keyboard is, and how to orient the keyboard layout. - - -To enable this method, add the following to your `config.h` file: - -```c -#define EE_HANDS -``` - -Next, you will have to flash the correct handedness option to the controller on each halve. You can do this manually with the following bootloader targets using `qmk flash -kb -km -bl ` command to flash: - -|Microcontroller Type|Bootloader Parameter| -|--------------------|--------------------| -|AVR controllers with Caterina bootloader
(e.g. Pro Micro)|`avrdude-split-left`
`avrdude-split-right`| -|AVR controllers with the stock Amtel DFU or DFU compatible bootloader
(e.g. Elite-C)|`dfu-split-left`
`dfu-split-right`| -|ARM controllers with a DFU compatible bootloader
(e.g. Proton-C)|`dfu-util-split-left`
`dfu-util-split-right`| -|ARM controllers with a UF2 compatible bootloader
(e.g. RP2040)|`uf2-split-left`
`uf2-split-right`| - -Example for `crkbd/rev1` keyboard with normal AVR Pro Micro MCUs, reset the left controller and run: -``` -qmk flash -kb crkbd/rev1 -km default -bl avrdude-split-left -``` -Reset the right controller and run: -``` -qmk flash -kb crkbd/rev1 -km default -bl avrdude-split-right -``` - -::: tip -Some controllers (e.g. Blackpill with DFU compatible bootloader) will need to be flashed with handedness bootloader parameter every time because it is not retained between flashes. -::: - -::: tip -[QMK Toolbox]() can also be used to flash EEPROM handedness files. Place the controller in bootloader mode and select menu option Tools -> EEPROM -> Set Left/Right Hand -::: - -This setting is not changed when re-initializing the EEPROM using the `EE_CLR` key, or using the `eeconfig_init()` function. However, if you reset the EEPROM outside of the firmware's built in options (such as flashing a file that overwrites the `EEPROM`, like how the [QMK Toolbox]()'s "Reset EEPROM" button works), you'll need to re-flash the controller with the `EEPROM` files. - -You can find the `EEPROM` files in the QMK firmware repo, [here](https://github.com/qmk/qmk_firmware/tree/master/quantum/split_common). - - -#### Handedness by `#define` - -You can use this option when USB cable is always connected to just one side of the split keyboard. - -If the USB cable is always connected to the right side, add the following to your `config.h` file and flash both sides with this option: -```c -#define MASTER_RIGHT -``` - -If the USB cable is always connected to the left side, add the following to your `config.h` file and flash both sides with this option: -```c -#define MASTER_LEFT -``` - -::: tip -If neither options are defined, the handedness defaults to `MASTER_LEFT`. -::: - - -### Communication Options - -Because not every split keyboard is identical, there are a number of additional options that can be configured in your `config.h` file. - -```c -#define USE_I2C -``` - -This configures the use of I2C support for split keyboard transport (AVR only). - -```c -#define SOFT_SERIAL_PIN D0 -``` - -This sets the pin to be used for serial communication. If you're not using serial, you shouldn't need to define this. - -However, if you are using serial and I2C on the board, you will need to set this, and to something other than D0 and D1 (as these are used for I2C communication). - -```c -#define SELECT_SOFT_SERIAL_SPEED {#} -``` - -If you're having issues with serial communication, you can change this value, as it controls the communication speed for serial. The default is 1, and the possible values are: - -* **`0`**: about 189kbps (Experimental only) -* **`1`**: about 137kbps (default) -* **`2`**: about 75kbps -* **`3`**: about 39kbps -* **`4`**: about 26kbps -* **`5`**: about 20kbps - -```c -#define FORCED_SYNC_THROTTLE_MS 100 -``` - -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_MAX_CONNECTION_ERRORS 10 -``` -This sets the maximum number of failed communication attempts (one per scan cycle) from the master part before it assumes that no slave part is connected. This makes it possible to use a master part without the slave part connected. - -Set to 0 to disable the disconnection check altogether. - -```c -#define SPLIT_CONNECTION_CHECK_TIMEOUT 500 -``` -How long (in milliseconds) the master part should block all connection attempts to the slave after the communication has been flagged as disconnected (see `SPLIT_MAX_CONNECTION_ERRORS` above). - -One communication attempt will be allowed everytime this amount of time has passed since the last attempt. If that attempt succeeds, the communication is seen as working again. - -Set to 0 to disable this throttling of communications while disconnected. This can save you a couple of bytes of firmware size. - - -### Data Sync Options - -The following sync options add overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled. These can be enabled by adding the chosen option(s) to your `config.h` file. - -```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. The purpose of this feature is to support cosmetic use of key events (e.g. RGB reacting to keypresses). - -```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. - -```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. - -```c -#define SPLIT_MODS_ENABLE -``` - -This enables transmitting modifier state (normal, weak, oneshot and oneshot locked) 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). - -```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). - -```c -#define SPLIT_OLED_ENABLE -``` - -This enables transmitting the current OLED on/off status to the slave side of the split keyboard. The purpose of this feature is to support state (on/off state only) syncing. - -```c -#define SPLIT_ST7565_ENABLE -``` - -This enables transmitting the current ST7565 on/off status to the slave side of the split keyboard. The purpose of this feature is to support state (on/off state only) syncing. - -```c -#define SPLIT_POINTING_ENABLE -``` - -This enables transmitting the pointing device status to the master side of the split keyboard. The purpose of this feature is to enable use pointing devices on the slave side. - -::: warning -There is additional required configuration for `SPLIT_POINTING_ENABLE` outlined in the [pointing device documentation](feature_pointing_device#split-keyboard-configuration). -::: - -```c -#define SPLIT_HAPTIC_ENABLE -``` - -This enables the triggering of haptic feedback on the slave side of the split keyboard. This will send information to the slave side such as the mode, dwell, and whether buzz is enabled. - -```c -#define SPLIT_ACTIVITY_ENABLE -``` - -This synchronizes the activity timestamps between sides of the split keyboard, allowing for activity timeouts to occur. - -### Custom data sync between sides {#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"); - } - } - } -} -``` - -::: warning -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 - -There are some settings that you may need to configure, based on how the hardware is set up. - -```c -#define MATRIX_ROW_PINS_RIGHT { } -#define MATRIX_COL_PINS_RIGHT { } -``` - -This allows you to specify a different set of pins for the matrix on the right side. This is useful if you have a board with differently-shaped halves that requires a different configuration (such as Keebio's Quefrency). The number of pins in the right and left matrices must be the same, if you have a board with a different number of rows or columns on one side, pad out the extra spaces with `NO_PIN` and make sure you add the unused rows or columns to your matrix. - -```c -#define DIRECT_PINS_RIGHT { { F1, F0, B0, C7 }, { F4, F5, F6, F7 } } -``` - -This allows you to specify a different set of direct pins for the right side. - -```c -#define ENCODERS_PAD_A_RIGHT { encoder1a, encoder2a } -#define ENCODERS_PAD_B_RIGHT { encoder1b, encoder2b } -``` - -This allows you to specify a different set of encoder pins for the right side. - -```c -#define RGBLIGHT_SPLIT -``` - -This option enables synchronization of the RGB Light modes between the controllers of the split keyboard. This is for keyboards that have RGB LEDs that are directly wired to the controller (that is, they are not using the "extra data" option on the TRRS cable). - -```c -#define RGBLED_SPLIT { 6, 6 } -``` - -This sets how many LEDs are directly connected to each controller. The first number is the left side, and the second number is the right side. - -::: tip -This setting implies that `RGBLIGHT_SPLIT` is enabled, and will forcibly enable it, if it's not. -::: - - -```c -#define SPLIT_USB_DETECT -``` - -Enabling this option changes the startup behavior to listen for an active USB communication to delegate which part is master and which is slave. With this option enabled and theres's USB communication, then that half assumes it is the master, otherwise it assumes it is the slave. - -Without this option, the master is the half that can detect voltage on the physical USB connection (VBUS detection). - -Enabled by default on ChibiOS/ARM. - -::: tip -This setting will stop the ability to demo using battery packs. -::: - -```c -#define SPLIT_USB_TIMEOUT 2000 -``` -This sets the maximum timeout when detecting master/slave when using `SPLIT_USB_DETECT`. - -```c -#define SPLIT_USB_TIMEOUT_POLL 10 -``` -This sets the poll frequency when detecting master/slave when using `SPLIT_USB_DETECT` - -```c -#define SPLIT_WATCHDOG_ENABLE -``` - -This will enable a software watchdog on any side delegated as slave and will reboot the keyboard if no successful communication occurs within `SPLIT_WATCHDOG_TIMEOUT`. This can be particularly helpful when `SPLIT_USB_DETECT` delegates both sides as slave in some circumstances. - -```c -#define SPLIT_WATCHDOG_TIMEOUT 3000 -``` -This set the maximum slave timeout when waiting for communication from master when using `SPLIT_WATCHDOG_ENABLE` - -## Hardware Considerations and Mods - -Master/slave delegation is made either by detecting voltage on VBUS connection or waiting for USB communication (`SPLIT_USB_DETECT`). Pro Micro boards can use VBUS detection out of the box and be used with or without `SPLIT_USB_DETECT`. - -Many ARM boards, but not all, do not support VBUS detection. Because it is common that ARM boards lack VBUS detection, `SPLIT_USB_DETECT` is automatically defined on ARM targets (technically when ChibiOS is targetted). - -### Teensy boards - -Teensy boards lack VBUS detection out of the box and must have `SPLIT_USB_DETECT` defined. With the Teensy 2.0 and Teensy++ 2.0, there is a simple hardware mod that you can perform to add VBUS detection, so you don't need the `SPLIT_USB_DETECT` option. - -You'll only need a few things: - -* A knife (x-acto knife, ideally) -* A solder station or hot air station -* An appropriate Schottky diode, such as the [PMEG2005EH](https://www.digikey.com/en/products/detail/nexperia-usa-inc/PMEG2005EH,115/1589924) - -You'll need to cut the small trace between the 5V and center pads on the back of the Teensy. - -Once you have done that, you will want to solder the diode from the 5V pad to the center pad. - -You may need to use the 5V pad from the regulator block above as the pads were too small and placed too closely together to place the Schottky diode properly. - -![Teensy++ 2.0](https://i.imgur.com/BPEC5n5.png) - -## Additional Resources - -Nicinabox has a [very nice and detailed guide](https://github.com/nicinabox/lets-split-guide) for the Let's Split keyboard, that covers most everything you need to know, including troubleshooting information. - -However, the RGB Light section is out of date, as it was written long before the RGB Split code was added to QMK Firmware. Instead, wire each strip up directly to the controller. - - diff --git a/docs/feature_st7565.md b/docs/feature_st7565.md deleted file mode 100644 index de3e44d8e9..0000000000 --- a/docs/feature_st7565.md +++ /dev/null @@ -1,274 +0,0 @@ -# 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.
`(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.
`(sizeof(ST7565_BLOCK_TYPE) * 8)`.| -|`ST7565_BLOCK_SIZE` |`32` |The size of each block for dirty rendering
`(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/feature_stenography.md b/docs/feature_stenography.md deleted file mode 100644 index 6827117a6b..0000000000 --- a/docs/feature_stenography.md +++ /dev/null @@ -1,221 +0,0 @@ -# Stenography in QMK {#stenography-in-qmk} - -[Stenography](https://en.wikipedia.org/wiki/Stenotype) is a method of writing most often used by court reports, closed-captioning, and real-time transcription for the deaf. In stenography words are chorded syllable by syllable with a mixture of spelling, phonetic, and shortcut (briefs) strokes. Professional stenographers can reach 200-300 WPM without any of the strain usually found in standard typing and with far fewer errors (>99.9% accuracy). - -The [Open Steno Project](https://www.openstenoproject.org/) has built an open-source program called Plover that provides real-time translation of steno strokes into words and commands. It has an established dictionary and supports - -## Plover with QWERTY Keyboard {#plover-with-qwerty-keyboard} - -Plover can work with any standard QWERTY keyboard, although it is more efficient if the keyboard supports NKRO (n-key rollover) to allow Plover to see all the pressed keys at once. An example keymap for Plover can be found in `planck/keymaps/default`. Switching to the `PLOVER` layer adjusts the position of the keyboard to support the number bar. - -To enable NKRO, add `NKRO_ENABLE = yes` in your `rules.mk` and make sure to press `NK_ON` to turn it on because `NKRO_ENABLE = yes` merely adds the possibility of switching to NKRO mode but it doesn't automatically switch to it. If you want to automatically switch, add `#define FORCE_NKRO` in your `config.h`. - -You may also need to adjust your layout, either in QMK or in Plover, if you have anything other than a standard layout. You may also want to purchase some steno-friendly keycaps to make it easier to hit multiple keys. - -## Plover with Steno Protocol {#plover-with-steno-protocol} - -Plover also understands the language of several steno machines. QMK can speak a couple of these languages: TX Bolt and GeminiPR. An example layout can be found in `planck/keymaps/steno`. - -When QMK speaks to Plover over a steno protocol, Plover will not use the keyboard as input. This means that you can switch back and forth between a standard keyboard and your steno keyboard, or even switch layers from Plover to standard and back without needing to activate/deactivate Plover. - -In this mode, Plover expects to speak with a steno machine over a serial port so QMK will present itself to the operating system as a virtual serial port in addition to a keyboard. - -::: info -Note: Due to hardware limitations, you might not be able to run both a virtual serial port and mouse emulation at the same time. -::: - -::: warning -Serial stenography protocols are not supported on [V-USB keyboards](compatible_microcontrollers#atmel-avr). -::: - -To enable stenography protocols, add the following lines to your `rules.mk`: -```make -STENO_ENABLE = yes -``` - -### TX Bolt {#tx-bolt} - -TX Bolt communicates the status of 24 keys over a simple protocol in variable-sized (1–4 bytes) packets. - -To select TX Bolt, add the following lines to your `rules.mk`: -```make -STENO_ENABLE = yes -STENO_PROTOCOL = txbolt -``` - -Each byte of the packet represents a different group of steno keys. Determining the group of a certain byte of the packet is done by checking the first two bits, the remaining bits are set if the corresponding steno key was pressed for the stroke. The last set of keys (as indicated by leading `11`) needs to keep track of less keys than there are bits so one of the bits is constantly 0. - -The start of a new packet can be detected by comparing the group “ID” (the two MSBs) of the current byte to that of the previously received byte. If the group “ID” of the current byte is smaller or equal to that of the previous byte, it means that the current byte is the beginning of a new packet. - -The format of TX Bolt packets is shown below. -``` -00HWPKTS 01UE*OAR 10GLBPRF 110#ZDST -``` - -Examples of steno strokes and the associated packet: -- `EUBG` = `01110000 10101000` -- `WAZ` = `00010000 01000010 11001000` -- `PHAPBGS` = `00101000 01000010 10101100 11000010` - -### GeminiPR {#geminipr} - -GeminiPR encodes 42 keys into a 6-byte packet. While TX Bolt contains everything that is necessary for standard stenography, GeminiPR opens up many more options, including differentiating between top and bottom `S-`, and supporting non-English theories. - -To select GeminiPR, add the following lines to your `rules.mk`: -```make -STENO_ENABLE = yes -STENO_PROTOCOL = geminipr -``` - -All packets in the GeminiPR protocol consist of exactly six bytes, used as bit-arrays for different groups of keys. The beginning of a packet is indicated by setting the most significant bit (MSB) to 1 while setting the MSB of the remaining five bytes to 0. - -The format of GeminiPR packets is shown below. -``` -1 Fn #1 #2 #3 #4 #5 #6 -0 S1- S2- T- K- P- W- H- -0 R- A- O- *1 *2 res1 res2 -0 pwr *3 *4 -E -U -F -R -0 -P -B -L -G -T -S -D -0 #7 #8 #9 #A #B #C -Z -``` - -Examples of steno strokes and the associated packet: -- `EUBG` = `10000000 00000000 00000000 00001100 00101000 00000000` -- `WAZ` = `10000000 00000010 00100000 00000000 00000000 00000001` -- `PHAPBGS` = `10000000 00000101 00100000 00000000 01101010 00000000` - -### Switching protocols on the fly {#switching-protocols-on-the-fly} - -If you wish to switch the serial protocol used to transfer the steno chords without having to recompile your keyboard firmware every time, you can press the `QK_STENO_BOLT` and `QK_STENO_GEMINI` keycodes in order to switch protocols on the fly. - -To enable these special keycodes, add the following lines to your `rules.mk`: -```make -STENO_ENABLE = yes -STENO_PROTOCOL = all -``` - -If you want to switch protocols programatically, as part of a custom macro for example, don't use `tap_code(QK_STENO_*)`, as `tap_code` only supports [basic keycodes](keycodes_basic). Instead, you should use `steno_set_mode(STENO_MODE_*)`, whose valid arguments are `STENO_MODE_BOLT` and `STENO_MODE_GEMINI`. - -The default protocol is Gemini PR but the last protocol used is stored in non-volatile memory so QMK will remember your choice between reboots of your keyboard — assuming that your keyboard features (emulated) EEPROM. - -Naturally, this option takes the most amount of firmware space as it needs to compile the code for all the available stenography protocols. In most cases, compiling a single stenography protocol is sufficient. - -The default value for `STENO_PROTOCOL` is `all`. - -## Configuring QMK for Steno {#configuring-qmk-for-steno} - -After enabling stenography and optionally selecting a protocol, you may also need disable mouse keys, extra keys, or another USB endpoint to prevent conflicts. The builtin USB stack for some processors only supports a certain number of USB endpoints and the virtual serial port needed for steno fills 3 of them. - -::: warning -If you had *explicitly* set `VIRSTER_ENABLE = no`, none of the serial stenography protocols (GeminiPR, TX Bolt) will work properly. You are expected to either set it to `yes`, remove the line from your `rules.mk` or send the steno chords yourself in an alternative way using the [provided interceptable hooks](#interfacing-with-the-code). -::: - -In your keymap, create a new layer for Plover, that you can fill in with the [steno keycodes](#keycode-reference). Remember to create a key to switch to the layer as well as a key for exiting the layer. - -Once you have your keyboard flashed, launch Plover. Click the 'Configure...' button. In the 'Machine' tab, select the Stenotype Machine that corresponds to your desired protocol. Click the 'Configure...' button on this tab and enter the serial port or click 'Scan'. Baud rate is fine at 9600 (although you should be able to set as high as 115200 with no issues). Use the default settings for everything else (Data Bits: 8, Stop Bits: 1, Parity: N, no flow control). - -To test your keymap, you can chord keys on your keyboard and either look at the output of the 'paper tape' (Tools > Paper Tape) or that of the 'layout display' (Tools > Layout Display). If your strokes correctly show up, you are now ready to steno! - -## Learning Stenography {#learning-stenography} - -* [Learn Plover!](https://sites.google.com/site/learnplover/) -* [Steno Jig](https://joshuagrams.github.io/steno-jig/) -* More resources at the Plover [Learning Stenography](https://github.com/openstenoproject/plover/wiki/Learning-Stenography) wiki - -## Interfacing with the code {#interfacing-with-the-code} - -The steno code has three interceptable hooks. If you define these functions, they will be called at certain points in processing; if they return true, processing continues, otherwise it's assumed you handled things. - -```c -bool send_steno_chord_user(steno_mode_t mode, uint8_t chord[MAX_STROKE_SIZE]); -``` - -This function is called when a chord is about to be sent. Mode will be one of `STENO_MODE_BOLT` or `STENO_MODE_GEMINI`. This represents the actual chord that would be sent via whichever protocol. You can modify the chord provided to alter what gets sent. Remember to return true if you want the regular sending process to happen. - -```c -bool process_steno_user(uint16_t keycode, keyrecord_t *record) { return true; } -``` - -This function is called when a keypress has come in, before it is processed. The keycode should be one of `QK_STENO_BOLT`, `QK_STENO_GEMINI`, or one of the `STN_*` key values. - -```c -bool post_process_steno_user(uint16_t keycode, keyrecord_t *record, steno_mode_t mode, uint8_t chord[MAX_STROKE_SIZE], int8_t n_pressed_keys); -``` - -This function is called after a key has been processed, but before any decision about whether or not to send a chord. This is where to put hooks for things like, say, live displays of steno chords or keys. - -If `record->event.pressed` is false, and `n_pressed_keys` is 0 or 1, the chord will be sent shortly, but has not yet been sent. This relieves you of the need of keeping track of where a packet ends and another begins. - -The `chord` argument contains the packet of the current chord as specified by the protocol in use. This is *NOT* simply a list of chorded steno keys of the form `[STN_E, STN_U, STN_BR, STN_GR]`. Refer to the appropriate protocol section of this document to learn more about the format of the packets in your steno protocol/mode of choice. - -The `n_pressed_keys` argument is the number of physical keys actually being held down. -This is not always equal to the number of bits set to 1 (aka the [Hamming weight](https://en.wikipedia.org/wiki/Hamming_weight)) in `chord` because it is possible to simultaneously press down four keys, then release three of those four keys and then press yet another key while the fourth finger is still holding down its key. -At the end of this scenario given as an example, `chord` would have five bits set to 1 but -`n_pressed_keys` would be set to 2 because there are only two keys currently being pressed down. - -## Keycode Reference {#keycode-reference} - -::: info -Note: TX Bolt does not support the full set of keys. The TX Bolt implementation in QMK will map the GeminiPR keys to the nearest TX Bolt key so that one key map will work for both. -::: - -|GeminiPR|TX Bolt|Steno Key| -|--------|-------|-----------| -|`STN_N1`|`STN_NUM`|Number bar #1| -|`STN_N2`|`STN_NUM`|Number bar #2| -|`STN_N3`|`STN_NUM`|Number bar #3| -|`STN_N4`|`STN_NUM`|Number bar #4| -|`STN_N5`|`STN_NUM`|Number bar #5| -|`STN_N6`|`STN_NUM`|Number bar #6| -|`STN_N7`|`STN_NUM`|Number bar #7| -|`STN_N8`|`STN_NUM`|Number bar #8| -|`STN_N9`|`STN_NUM`|Number bar #9| -|`STN_NA`|`STN_NUM`|Number bar #A| -|`STN_NB`|`STN_NUM`|Number bar #B| -|`STN_NC`|`STN_NUM`|Number bar #C| -|`STN_S1`|`STN_SL`| `S-` upper| -|`STN_S2`|`STN_SL`| `S-` lower| -|`STN_TL`|`STN_TL`| `T-`| -|`STN_KL`|`STN_KL`| `K-`| -|`STN_PL`|`STN_PL`| `P-`| -|`STN_WL`|`STN_WL`| `W-`| -|`STN_HL`|`STN_HL`| `H-`| -|`STN_RL`|`STN_RL`| `R-`| -|`STN_A`|`STN_A`| `A` vowel| -|`STN_O`|`STN_O`| `O` vowel| -|`STN_ST1`|`STN_STR`| `*` upper-left | -|`STN_ST2`|`STN_STR`| `*` lower-left| -|`STN_ST3`|`STN_STR`| `*` upper-right| -|`STN_ST4`|`STN_STR`| `*` lower-right| -|`STN_E`|`STN_E`| `E` vowel| -|`STN_U`|`STN_U`| `U` vowel| -|`STN_FR`|`STN_FR`| `-F`| -|`STN_RR`|`STN_RR`| `-R`| -|`STN_PR`|`STN_PR`| `-P`| -|`STN_BR`|`STN_BR`| `-B`| -|`STN_LR`|`STN_LR`| `-L`| -|`STN_GR`|`STN_GR`| `-G`| -|`STN_TR`|`STN_TR`| `-T`| -|`STN_SR`|`STN_SR`| `-S`| -|`STN_DR`|`STN_DR`| `-D`| -|`STN_ZR`|`STN_ZR`| `-Z`| -|`STN_FN`|| (Function)| -|`STN_RES1`||(Reset 1)| -|`STN_RES2`||(Reset 2)| -|`STN_PWR`||(Power)| - -If you do not want to hit two keys with one finger combined keycodes can be used. These cause both keys to be reported as pressed or released. To use these keycodes define `STENO_COMBINEDMAP` in your `config.h` file. - -|Combined key | Key1 | Key 2 | -|---------------|--------|----------| -|STN_S3 | STN_S1 | STN_S2 | -|STN_TKL | STN_TL | STN_KL | -|STN_PWL | STN_PL | STN_WL | -|STN_HRL | STN_HL | STN_RL | -|STN_FRR | STN_FR | STN_RR | -|STN_PBR | STN_PR | STN_BR | -|STN_LGR | STN_LR | STN_GR | -|STN_TSR | STN_TR | STN_SR | -|STN_DZR | STN_DR | STN_ZR | -|STN_AO | STN_A | STN_O | -|STN_EU | STN_E | STN_U | diff --git a/docs/feature_swap_hands.md b/docs/feature_swap_hands.md deleted file mode 100644 index 7546823d84..0000000000 --- a/docs/feature_swap_hands.md +++ /dev/null @@ -1,57 +0,0 @@ -# Swap-Hands Action - -The swap-hands action allows support for one-handed typing without requiring a separate layer. Set `SWAP_HANDS_ENABLE` in the Makefile and define a `hand_swap_config` entry in your keymap. Now whenever the `ACTION_SWAP_HANDS` command key is pressed the keyboard is mirrored. For instance, to type "Hello, World" on QWERTY you would type `^Ge^s^s^w^c W^wr^sd` - -## Configuration - -The configuration table is a simple 2-dimensional array to map from column/row to new column/row. Example `hand_swap_config` for Planck: - -```c -const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { - {{11, 0}, {10, 0}, {9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}}, - {{11, 1}, {10, 1}, {9, 1}, {8, 1}, {7, 1}, {6, 1}, {5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}}, - {{11, 2}, {10, 2}, {9, 2}, {8, 2}, {7, 2}, {6, 2}, {5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}}, - {{11, 3}, {10, 3}, {9, 3}, {8, 3}, {7, 3}, {6, 3}, {5, 3}, {4, 3}, {3, 3}, {2, 3}, {1, 3}, {0, 3}}, -}; -``` - -Note that the array indices are reversed same as the matrix and the values are of type `keypos_t` which is `{col, row}` and all values are zero-based. In the example above, `hand_swap_config[2][4]` (third row, fifth column) would return `{7, 2}` (third row, eighth column). Yes, this is confusing. - -## Swap Keycodes - -|Key |Aliases |Description | -|-----------------------------|---------|----------------------------------------------------| -|`SH_T(kc)` | |Momentary swap when held, `kc` when tapped | -|`QK_SWAP_HANDS_ON` |`SH_ON` |Turn on hand swap | -|`QK_SWAP_HANDS_OFF` |`SH_OFF` |Turn off hand swap | -|`QK_SWAP_HANDS_MOMENTARY_ON` |`SH_MON` |Turn on hand swap while held | -|`QK_SWAP_HANDS_MOMENTARY_OFF`|`SH_MOFF`|Turn off hand swap while held | -|`QK_SWAP_HANDS_TOGGLE` |`SH_TOGG`|Toggle hand swap | -|`QK_SWAP_HANDS_TAP_TOGGLE` |`SH_TT` |Momentary swap when held, toggle when tapped | -|`QK_SWAP_HANDS_ONE_SHOT` |`SH_OS` |Turn on hand swap while held or until next key press| - -`SH_TT` swap-hands tap-toggle key is similar to [layer tap-toggle](feature_layers#switching-and-toggling-layers). Tapping repeatedly (5 taps by default) will toggle swap-hands on or off, like `SH_TOGG`. Tap-toggle count can be changed by defining a value for `TAPPING_TOGGLE`. - -## Encoder Mapping - -When using an encoder mapping, it's also able to handle swapping encoders between sides, too. - -Encoder indexes are defined as left-to-right, and the extent of the array needs to match the number of encoders on the keyboard. - -As an example, if a split keyboard has a single encoder per side, you can swap the order by using the following code in your keymap: -```c -#if defined(SWAP_HANDS_ENABLE) && defined(ENCODER_MAP_ENABLE) -const uint8_t PROGMEM encoder_hand_swap_config[NUM_ENCODERS] = { 1, 0 }; -#endif -``` - -### Functions {#functions} - -User callback functions to manipulate Swap-Hands: - -| Function | Description | -|-----------------------|---------------------------------------------| -| `swap_hands_on()` | Turns Swap-Hands on. | -| `swap_hands_off()` | Turns Swap-Hands off. | -| `swap_hands_toggle()` | Toggles Swap-Hands. | -| `is_swap_hands_on()` | Returns true if Swap-Hands is currently on. | diff --git a/docs/feature_tap_dance.md b/docs/feature_tap_dance.md deleted file mode 100644 index e43daf4196..0000000000 --- a/docs/feature_tap_dance.md +++ /dev/null @@ -1,567 +0,0 @@ -# Tap Dance: A Single Key Can Do 3, 5, or 100 Different Things - -## Introduction {#introduction} - -Hit the semicolon key once, send a semicolon. Hit it twice, rapidly -- send a colon. Hit it three times, and your keyboard's LEDs do a wild dance. That's just one example of what Tap Dance can do. It's one of the nicest community-contributed features in the firmware, conceived and created by [algernon](https://github.com/algernon) in [#451](https://github.com/qmk/qmk_firmware/pull/451). Here's how algernon describes the feature: - -With this feature one can specify keys that behave differently, based on the amount of times they have been tapped, and when interrupted, they get handled before the interrupter. - -## How to Use Tap Dance {#how-to-use} - -First, you will need `TAP_DANCE_ENABLE = yes` in your `rules.mk`, because the feature is disabled by default. This adds a little less than 1k to the firmware size. - -Optionally, you might want to set a custom `TAPPING_TERM` time by adding something like this in your `config.h` file: - -```c -#define TAPPING_TERM 175 -#define TAPPING_TERM_PER_KEY -``` - -The `TAPPING_TERM` time is the maximum time allowed between taps of your Tap Dance key, and is measured in milliseconds. For example, if you used the above `#define` statement and set up a Tap Dance key that sends `Space` on single-tap and `Enter` on double-tap, then this key will send `ENT` only if you tap this key twice in less than 175ms. If you tap the key, wait more than 175ms, and tap the key again you'll end up sending `SPC SPC` instead. The `TAPPING_TERM_PER_KEY` definition is only needed if you control the tapping term through a [custom `get_tapping_term` function](tap_hold#tapping_term), which may be needed because `TAPPING_TERM` affects not just tap-dance keys. - -Next, you will want to define some tap-dance keys, which is easiest to do with the `TD()` macro. That macro takes a number which will later be used as an index into the `tap_dance_actions` array and turns it into a tap-dance keycode. - -After this, you'll want to use the `tap_dance_actions` array to specify what actions shall be taken when a tap-dance key is in action. Currently, there are five possible options: - -* `ACTION_TAP_DANCE_DOUBLE(kc1, kc2)`: Sends the `kc1` keycode when tapped once, `kc2` otherwise. When the key is held, the appropriate keycode is registered: `kc1` when pressed and held, `kc2` when tapped once, then pressed and held. -* `ACTION_TAP_DANCE_LAYER_MOVE(kc, layer)`: Sends the `kc` keycode when tapped once, or moves to `layer`. (this functions like the `TO` layer keycode). -* `ACTION_TAP_DANCE_LAYER_TOGGLE(kc, layer)`: Sends the `kc` keycode when tapped once, or toggles the state of `layer`. (this functions like the `TG` layer keycode). -* `ACTION_TAP_DANCE_FN(fn)`: Calls the specified function - defined in the user keymap - with the final tap count of the tap dance action. -* `ACTION_TAP_DANCE_FN_ADVANCED(on_each_tap_fn, on_dance_finished_fn, on_dance_reset_fn)`: Calls the first specified function - defined in the user keymap - on every tap, the second function when the dance action finishes (like the previous option), and the last function when the tap dance action resets. -* `ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(on_each_tap_fn, on_each_release_fn, on_dance_finished_fn, on_dance_reset_fn)`: This macro is identical to `ACTION_TAP_DANCE_FN_ADVANCED` with the addition of `on_each_release_fn` which is invoked every time the key for the tap dance is released. It is worth noting that `on_each_release_fn` will still be called even when the key is released after the dance finishes (e.g. if the key is released after being pressed and held for longer than the `TAPPING_TERM`). - -The first option is enough for a lot of cases, that just want dual roles. For example, `ACTION_TAP_DANCE_DOUBLE(KC_SPC, KC_ENT)` will result in `Space` being sent on single-tap, `Enter` otherwise. - -::: warning -Keep in mind that only [basic keycodes](keycodes_basic) are supported here. Custom keycodes are not supported. -::: - -Similar to the first option, the second and third option are good for simple layer-switching cases. - -For more complicated cases, like blink the LEDs, fiddle with the backlighting, and so on, use the fourth or fifth option. Examples of each are listed below. - -## Implementation Details {#implementation} - -Well, that's the bulk of it! You should now be able to work through the examples below, and to develop your own Tap Dance functionality. But if you want a deeper understanding of what's going on behind the scenes, then read on for the explanation of how it all works! - -Let's go over the three functions mentioned in `ACTION_TAP_DANCE_FN_ADVANCED` in a little more detail. They all receive the same two arguments: a pointer to a structure that holds all dance related state information, and a pointer to a use case specific state variable. The three functions differ in when they are called. The first, `on_each_tap_fn()`, is called every time the tap dance key is *pressed*. Before it is called, the counter is incremented and the timer is reset. The second function, `on_dance_finished_fn()`, is called when the tap dance is interrupted or ends because `TAPPING_TERM` milliseconds have passed since the last tap. When the `finished` field of the dance state structure is set to `true`, the `on_dance_finished_fn()` is skipped. After `on_dance_finished_fn()` was called or would have been called, but no sooner than when the tap dance key is *released*, `on_dance_reset_fn()` is called. It is possible to end a tap dance immediately, skipping `on_dance_finished_fn()`, but not `on_dance_reset_fn`, by calling `reset_tap_dance(state)`. - -To accomplish this logic, the tap dance mechanics use three entry points. The main entry point is `process_tap_dance()`, called from `process_record_quantum()` *after* `process_record_kb()` and `process_record_user()`. This function is responsible for calling `on_each_tap_fn()` and `on_dance_reset_fn()`. In order to handle interruptions of a tap dance, another entry point, `preprocess_tap_dance()` is run right at the beginning of `process_record_quantum()`. This function checks whether the key pressed is a tap-dance key. If it is not, and a tap-dance was in action, we handle that first, and enqueue the newly pressed key. If it is a tap-dance key, then we check if it is the same as the already active one (if there's one active, that is). If it is not, we fire off the old one first, then register the new one. Finally, `tap_dance_task()` periodically checks whether `TAPPING_TERM` has passed since the last key press and finishes a tap dance if that is the case. - -This means that you have `TAPPING_TERM` time to tap the key again; you do not have to input all the taps within a single `TAPPING_TERM` timeframe. This allows for longer tap counts, with minimal impact on responsiveness. - -## Examples {#examples} - -### Simple Example: Send `ESC` on Single Tap, `CAPS_LOCK` on Double Tap {#simple-example} - -Here's a simple example for a single definition: - -1. In your `rules.mk`, add `TAP_DANCE_ENABLE = yes` -2. In your `keymap.c` file, define the variables and definitions, then add to your keymap: - -```c -// Tap Dance declarations -enum { - TD_ESC_CAPS, -}; - -// Tap Dance definitions -tap_dance_action_t tap_dance_actions[] = { - // Tap once for Escape, twice for Caps Lock - [TD_ESC_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_ESC, KC_CAPS), -}; - -// Add tap dance item to your keymap in place of a keycode -const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { - // ... - TD(TD_ESC_CAPS) - // ... -}; -``` - -### Complex Examples {#complex-examples} - -This section details several complex tap dance examples. -All the enums used in the examples are declared like this: - -```c -// Enums defined for all examples: -enum { - TD_ESC_CAPS, - CT_EGG, - CT_FLSH, - CT_CLN, - X_CTL, -}; -``` - -#### Example 1: Send "Safety Dance!" After 100 Taps {#example-1} - -```c -void dance_egg(tap_dance_state_t *state, void *user_data) { - if (state->count >= 100) { - SEND_STRING("Safety dance!"); - reset_tap_dance(state); - } -} - -tap_dance_action_t tap_dance_actions[] = { - [CT_EGG] = ACTION_TAP_DANCE_FN(dance_egg), -}; -``` - -#### Example 2: Turn LED Lights On Then Off, One at a Time {#example-2} - -```c -// On each tap, light up one LED, from right to left -// On the fourth tap, turn them off from right to left -void dance_flsh_each(tap_dance_state_t *state, void *user_data) { - switch (state->count) { - case 1: - ergodox_right_led_3_on(); - break; - case 2: - ergodox_right_led_2_on(); - break; - case 3: - ergodox_right_led_1_on(); - break; - case 4: - ergodox_right_led_3_off(); - wait_ms(50); - ergodox_right_led_2_off(); - wait_ms(50); - ergodox_right_led_1_off(); - } -} - -// On the fourth tap, set the keyboard on flash state -void dance_flsh_finished(tap_dance_state_t *state, void *user_data) { - if (state->count >= 4) { - reset_keyboard(); - } -} - -// If the flash state didn't happen, then turn off LEDs, left to right -void dance_flsh_reset(tap_dance_state_t *state, void *user_data) { - ergodox_right_led_1_off(); - wait_ms(50); - ergodox_right_led_2_off(); - wait_ms(50); - ergodox_right_led_3_off(); -} - -// All tap dances now put together. Example 2 is "CT_FLSH" -tap_dance_action_t tap_dance_actions[] = { - [TD_ESC_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_ESC, KC_CAPS), - [CT_EGG] = ACTION_TAP_DANCE_FN(dance_egg), - [CT_FLSH] = ACTION_TAP_DANCE_FN_ADVANCED(dance_flsh_each, dance_flsh_finished, dance_flsh_reset) -}; -``` - -#### Example 3: Send `:` on Tap, `;` on Hold {#example-3} - -With a little effort, powerful tap-hold configurations can be implemented as tap dances. To emit taps as early as possible, we need to act on releases of the tap dance key. There is no callback for this in the tap dance framework, so we use `process_record_user()`. - -```c -typedef struct { - uint16_t tap; - uint16_t hold; - uint16_t held; -} tap_dance_tap_hold_t; - -bool process_record_user(uint16_t keycode, keyrecord_t *record) { - tap_dance_action_t *action; - - switch (keycode) { - case TD(CT_CLN): // list all tap dance keycodes with tap-hold configurations - action = &tap_dance_actions[QK_TAP_DANCE_GET_INDEX(keycode)]; - if (!record->event.pressed && action->state.count && !action->state.finished) { - tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)action->user_data; - tap_code16(tap_hold->tap); - } - } - return true; -} - -void tap_dance_tap_hold_finished(tap_dance_state_t *state, void *user_data) { - tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data; - - if (state->pressed) { - if (state->count == 1 -#ifndef PERMISSIVE_HOLD - && !state->interrupted -#endif - ) { - register_code16(tap_hold->hold); - tap_hold->held = tap_hold->hold; - } else { - register_code16(tap_hold->tap); - tap_hold->held = tap_hold->tap; - } - } -} - -void tap_dance_tap_hold_reset(tap_dance_state_t *state, void *user_data) { - tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data; - - if (tap_hold->held) { - unregister_code16(tap_hold->held); - tap_hold->held = 0; - } -} - -#define ACTION_TAP_DANCE_TAP_HOLD(tap, hold) \ - { .fn = {NULL, tap_dance_tap_hold_finished, tap_dance_tap_hold_reset}, .user_data = (void *)&((tap_dance_tap_hold_t){tap, hold, 0}), } - -tap_dance_action_t tap_dance_actions[] = { - [CT_CLN] = ACTION_TAP_DANCE_TAP_HOLD(KC_COLN, KC_SCLN), -}; -``` - -#### Example 4: 'Quad Function Tap-Dance' {#example-4} - -By [DanielGGordon](https://github.com/danielggordon) - -Allow one key to have 4 (or more) functions, depending on number of presses, and if the key is held or tapped. -Below is a specific example: -* Tap = Send `x` -* Hold = Send `Control` -* Double Tap = Send `Escape` -* Double Tap and Hold = Send `Alt` - -You will need a few things that can be used for 'Quad Function Tap-Dance'. - -You'll need to add these to the top of your `keymap.c` file, before your keymap. - -```c -typedef enum { - TD_NONE, - TD_UNKNOWN, - TD_SINGLE_TAP, - TD_SINGLE_HOLD, - TD_DOUBLE_TAP, - TD_DOUBLE_HOLD, - TD_DOUBLE_SINGLE_TAP, // Send two single taps - TD_TRIPLE_TAP, - TD_TRIPLE_HOLD -} td_state_t; - -typedef struct { - bool is_press_action; - td_state_t state; -} td_tap_t; - -// Tap dance enums -enum { - X_CTL, - SOME_OTHER_DANCE -}; - -td_state_t cur_dance(tap_dance_state_t *state); - -// For the x tap dance. Put it here so it can be used in any keymap -void x_finished(tap_dance_state_t *state, void *user_data); -void x_reset(tap_dance_state_t *state, void *user_data); -``` - -Now, at the bottom of your `keymap.c` file, you'll need to add the following: - -```c -/* Return an integer that corresponds to what kind of tap dance should be executed. - * - * How to figure out tap dance state: interrupted and pressed. - * - * Interrupted: If the state of a dance is "interrupted", that means that another key has been hit - * under the tapping term. This is typically indicitive that you are trying to "tap" the key. - * - * Pressed: Whether or not the key is still being pressed. If this value is true, that means the tapping term - * has ended, but the key is still being pressed down. This generally means the key is being "held". - * - * One thing that is currenlty not possible with qmk software in regards to tap dance is to mimic the "permissive hold" - * feature. In general, advanced tap dances do not work well if they are used with commonly typed letters. - * For example "A". Tap dances are best used on non-letter keys that are not hit while typing letters. - * - * Good places to put an advanced tap dance: - * z,q,x,j,k,v,b, any function key, home/end, comma, semi-colon - * - * Criteria for "good placement" of a tap dance key: - * Not a key that is hit frequently in a sentence - * Not a key that is used frequently to double tap, for example 'tab' is often double tapped in a terminal, or - * in a web form. So 'tab' would be a poor choice for a tap dance. - * Letters used in common words as a double. For example 'p' in 'pepper'. If a tap dance function existed on the - * letter 'p', the word 'pepper' would be quite frustating to type. - * - * For the third point, there does exist the 'TD_DOUBLE_SINGLE_TAP', however this is not fully tested - * - */ -td_state_t cur_dance(tap_dance_state_t *state) { - if (state->count == 1) { - if (state->interrupted || !state->pressed) return TD_SINGLE_TAP; - // Key has not been interrupted, but the key is still held. Means you want to send a 'HOLD'. - else return TD_SINGLE_HOLD; - } else if (state->count == 2) { - // TD_DOUBLE_SINGLE_TAP is to distinguish between typing "pepper", and actually wanting a double tap - // action when hitting 'pp'. Suggested use case for this return value is when you want to send two - // keystrokes of the key, and not the 'double tap' action/macro. - if (state->interrupted) return TD_DOUBLE_SINGLE_TAP; - else if (state->pressed) return TD_DOUBLE_HOLD; - else return TD_DOUBLE_TAP; - } - - // Assumes no one is trying to type the same letter three times (at least not quickly). - // If your tap dance key is 'KC_W', and you want to type "www." quickly - then you will need to add - // an exception here to return a 'TD_TRIPLE_SINGLE_TAP', and define that enum just like 'TD_DOUBLE_SINGLE_TAP' - if (state->count == 3) { - if (state->interrupted || !state->pressed) return TD_TRIPLE_TAP; - else return TD_TRIPLE_HOLD; - } else return TD_UNKNOWN; -} - -// Create an instance of 'td_tap_t' for the 'x' tap dance. -static td_tap_t xtap_state = { - .is_press_action = true, - .state = TD_NONE -}; - -void x_finished(tap_dance_state_t *state, void *user_data) { - xtap_state.state = cur_dance(state); - switch (xtap_state.state) { - case TD_SINGLE_TAP: register_code(KC_X); break; - case TD_SINGLE_HOLD: register_code(KC_LCTL); break; - case TD_DOUBLE_TAP: register_code(KC_ESC); break; - case TD_DOUBLE_HOLD: register_code(KC_LALT); break; - // Last case is for fast typing. Assuming your key is `f`: - // For example, when typing the word `buffer`, and you want to make sure that you send `ff` and not `Esc`. - // In order to type `ff` when typing fast, the next character will have to be hit within the `TAPPING_TERM`, which by default is 200ms. - case TD_DOUBLE_SINGLE_TAP: tap_code(KC_X); register_code(KC_X); break; - default: break; - } -} - -void x_reset(tap_dance_state_t *state, void *user_data) { - switch (xtap_state.state) { - case TD_SINGLE_TAP: unregister_code(KC_X); break; - case TD_SINGLE_HOLD: unregister_code(KC_LCTL); break; - case TD_DOUBLE_TAP: unregister_code(KC_ESC); break; - case TD_DOUBLE_HOLD: unregister_code(KC_LALT); break; - case TD_DOUBLE_SINGLE_TAP: unregister_code(KC_X); break; - default: break; - } - xtap_state.state = TD_NONE; -} - -tap_dance_action_t tap_dance_actions[] = { - [X_CTL] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, x_finished, x_reset) -}; -``` - -And then simply use `TD(X_CTL)` anywhere in your keymap. - -::: info -In this configuration "hold" takes place **after** tap dance timeout. To achieve instant hold, remove `state->interrupted` checks in conditions. As a result you may use comfortable longer tapping periods to have more time for taps and not to wait too long for holds (try starting with doubled `TAPPING_TERM`). -::: - -#### Example 5: Using tap dance for advanced mod-tap and layer-tap keys {#example-5} - -Tap dance can be used to emulate `MT()` and `LT()` behavior when the tapped code is not a basic keycode. This is useful to send tapped keycodes that normally require `Shift`, such as parentheses or curly braces—or other modified keycodes, such as `Control + X`. - -Below your layers and custom keycodes, add the following: - -```c -// Tap Dance keycodes -enum td_keycodes { - ALT_LP // Our example key: `LALT` when held, `(` when tapped. Add additional keycodes for each tapdance. -}; - -// Define a type containing as many tapdance states as you need -typedef enum { - TD_NONE, - TD_UNKNOWN, - TD_SINGLE_TAP, - TD_SINGLE_HOLD, - TD_DOUBLE_SINGLE_TAP -} td_state_t; - -// Create a global instance of the tapdance state type -static td_state_t td_state; - -// Declare your tapdance functions: - -// Function to determine the current tapdance state -td_state_t cur_dance(tap_dance_state_t *state); - -// `finished` and `reset` functions for each tapdance keycode -void altlp_finished(tap_dance_state_t *state, void *user_data); -void altlp_reset(tap_dance_state_t *state, void *user_data); -``` - -Below your `LAYOUT`, define each of the tapdance functions: - -```c -// Determine the tapdance state to return -td_state_t cur_dance(tap_dance_state_t *state) { - if (state->count == 1) { - if (state->interrupted || !state->pressed) return TD_SINGLE_TAP; - else return TD_SINGLE_HOLD; - } - - if (state->count == 2) return TD_DOUBLE_SINGLE_TAP; - else return TD_UNKNOWN; // Any number higher than the maximum state value you return above -} - -// Handle the possible states for each tapdance keycode you define: - -void altlp_finished(tap_dance_state_t *state, void *user_data) { - td_state = cur_dance(state); - switch (td_state) { - case TD_SINGLE_TAP: - register_code16(KC_LPRN); - break; - case TD_SINGLE_HOLD: - register_mods(MOD_BIT(KC_LALT)); // For a layer-tap key, use `layer_on(_MY_LAYER)` here - break; - case TD_DOUBLE_SINGLE_TAP: // Allow nesting of 2 parens `((` within tapping term - tap_code16(KC_LPRN); - register_code16(KC_LPRN); - break; - default: - break; - } -} - -void altlp_reset(tap_dance_state_t *state, void *user_data) { - switch (td_state) { - case TD_SINGLE_TAP: - unregister_code16(KC_LPRN); - break; - case TD_SINGLE_HOLD: - unregister_mods(MOD_BIT(KC_LALT)); // For a layer-tap key, use `layer_off(_MY_LAYER)` here - break; - case TD_DOUBLE_SINGLE_TAP: - unregister_code16(KC_LPRN); - break; - default: - break; - } -} - -// Define `ACTION_TAP_DANCE_FN_ADVANCED()` for each tapdance keycode, passing in `finished` and `reset` functions -tap_dance_action_t tap_dance_actions[] = { - [ALT_LP] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, altlp_finished, altlp_reset) -}; -``` - -Wrap each tapdance keycode in `TD()` when including it in your keymap, e.g. `TD(ALT_LP)`. - -#### Example 6: Using tap dance for momentary-layer-switch and layer-toggle keys {#example-6} - -Tap Dance can be used to mimic MO(layer) and TG(layer) functionality. For this example, we will set up a key to function as `KC_QUOT` on single-tap, as `MO(_MY_LAYER)` on single-hold, and `TG(_MY_LAYER)` on double-tap. - -The first step is to include the following code towards the beginning of your `keymap.c`: - -```c -// Define a type for as many tap dance states as you need -typedef enum { - TD_NONE, - TD_UNKNOWN, - TD_SINGLE_TAP, - TD_SINGLE_HOLD, - TD_DOUBLE_TAP -} td_state_t; - -typedef struct { - bool is_press_action; - td_state_t state; -} td_tap_t; - -enum { - QUOT_LAYR, // Our custom tap dance key; add any other tap dance keys to this enum -}; - -// Declare the functions to be used with your tap dance key(s) - -// Function associated with all tap dances -td_state_t cur_dance(tap_dance_state_t *state); - -// Functions associated with individual tap dances -void ql_finished(tap_dance_state_t *state, void *user_data); -void ql_reset(tap_dance_state_t *state, void *user_data); -``` - -Towards the bottom of your `keymap.c`, include the following code: - -```c -// Determine the current tap dance state -td_state_t cur_dance(tap_dance_state_t *state) { - if (state->count == 1) { - if (!state->pressed) return TD_SINGLE_TAP; - else return TD_SINGLE_HOLD; - } else if (state->count == 2) return TD_DOUBLE_TAP; - else return TD_UNKNOWN; -} - -// Initialize tap structure associated with example tap dance key -static td_tap_t ql_tap_state = { - .is_press_action = true, - .state = TD_NONE -}; - -// Functions that control what our tap dance key does -void ql_finished(tap_dance_state_t *state, void *user_data) { - ql_tap_state.state = cur_dance(state); - switch (ql_tap_state.state) { - case TD_SINGLE_TAP: - tap_code(KC_QUOT); - break; - case TD_SINGLE_HOLD: - layer_on(_MY_LAYER); - break; - case TD_DOUBLE_TAP: - // Check to see if the layer is already set - if (layer_state_is(_MY_LAYER)) { - // If already set, then switch it off - layer_off(_MY_LAYER); - } else { - // If not already set, then switch the layer on - layer_on(_MY_LAYER); - } - break; - default: - break; - } -} - -void ql_reset(tap_dance_state_t *state, void *user_data) { - // If the key was held down and now is released then switch off the layer - if (ql_tap_state.state == TD_SINGLE_HOLD) { - layer_off(_MY_LAYER); - } - ql_tap_state.state = TD_NONE; -} - -// Associate our tap dance key with its functionality -tap_dance_action_t tap_dance_actions[] = { - [QUOT_LAYR] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, ql_finished, ql_reset) -}; - -// Set a long-ish tapping term for tap-dance keys -uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { - switch (keycode) { - case QK_TAP_DANCE ... QK_TAP_DANCE_MAX: - return 275; - default: - return TAPPING_TERM; - } -} -``` - -The above code is similar to that used in previous examples. The one point to note is that we need to be able to check which layers are active at any time so we can toggle them if needed. To do this we use the `layer_state_is(layer)` function which returns `true` if the given `layer` is active. - -The use of `cur_dance()` and `ql_tap_state` mirrors the above examples. - -The `case: TD_SINGLE_TAP` in `ql_finished` is similar to the above examples. The `TD_SINGLE_HOLD` case works in conjunction with `ql_reset()` to switch to `_MY_LAYER` while the tap dance key is held, and to switch away from `_MY_LAYER` when the key is released. This mirrors the use of `MO(_MY_LAYER)`. The `TD_DOUBLE_TAP` case works by checking whether `_MY_LAYER` is the active layer, and toggling it on or off accordingly. This mirrors the use of `TG(_MY_LAYER)`. - -`tap_dance_actions[]` works similar to the above examples. Note that, additionally, I set a longer tapping term for the tap dance keys. This is because I like my `TAPPING_TERM` to be short (\~175ms) for my non-tap-dance keys but find that this is too quick for me to reliably complete tap dance actions - thus the increased time of 275ms here. In order for the per-key tapping terms to take effect, `TAPPING_TERM_PER_KEY` must be defined in your `config.h`. - -Finally, to get this tap dance key working, be sure to include `TD(QUOT_LAYR)` in your `keymaps[]`. diff --git a/docs/feature_tri_layer.md b/docs/feature_tri_layer.md deleted file mode 100644 index a67ec97a89..0000000000 --- a/docs/feature_tri_layer.md +++ /dev/null @@ -1,50 +0,0 @@ -# Tri Layers {#tri-layers} - -This enables support for the OLKB style "Tri Layer" keycodes. These function similar to the `MO` (momentary) function key, but if both the "Lower" and "Upper" keys are pressed, it activates a third "Adjust" layer. To enable this functionality, add this line to your `rules.mk`: - -```make -TRI_LAYER_ENABLE = yes -``` - -Note that the "upper", "lower" and "adjust" names don't have a particular significance, they are just used to identify and clarify the behavior. Layers are processed from highest numeric value to lowest, however the values are not required to be consecutive. - -For a detailed explanation of how the layer stack works, check out [Keymap Overview](keymap#keymap-and-layers). - -## Keycodes {#keycodes} - -| Keycode | Alias | Description | -|----------------------|-----------|---------------------------------------------------------------------------------------------------------| -| `QK_TRI_LAYER_LOWER` | `TL_LOWR` | Momentarily enables the "lower" layer. Enables the "adjust" layer if the "upper" layer is also enabled" | -| `QK_TRI_LAYER_UPPER` | `TL_UPPR` | Momentarily enables the "upper" layer. Enables the "adjust" layer if the "lower" layer is also enabled" | - -## Configuration - -To change the default values for the layers, you can change these defines, in your `config.h` - -| Config name | Default | Description | -|--------------------------|---------|------------------------------------------| -| `TRI_LAYER_LOWER_LAYER` | `1` | Sets the default for the "lower" layer. | -| `TRI_LAYER_UPPER_LAYER` | `2` | Sets the default for the "upper" layer. | -| `TRI_LAYER_ADJUST_LAYER` | `3` | Sets the default for the "adjust" layer. | - -Eg, if you wanted to set the "Adjust" layer to be layer 5, you'd add this to your `config.h`: - -```c -#define TRI_LAYER_ADJUST_LAYER 5 -``` - -## Functions - -| Function name | Description | -|----------------------------------------------|-------------------------------------------------| -| `set_tri_layer_lower_layer(layer)` | Changes the "lower" layer*. | -| `set_tri_layer_upper_layer(layer)` | Changes the "upper" layer*. | -| `set_tri_layer_adjust_layer(layer)` | Changes the "adjust" layer*. | -| `set_tri_layer_layers(lower, upper, adjust)` | Sets the "lower", "upper" and "adjust" layers*. | -| `get_tri_layer_lower_layer()` | Gets the current "lower" layer. | -| `get_tri_layer_upper_layer()` | Gets the current "upper" layer. | -| `get_tri_layer_adjust_layer()` | Gets the current "adjust" layer. | - -::: warning -Note: these settings are not persistent, and will be reset to the default on power loss or power cycling of the controller. -::: diff --git a/docs/feature_unicode.md b/docs/feature_unicode.md deleted file mode 100644 index f9295c1f57..0000000000 --- a/docs/feature_unicode.md +++ /dev/null @@ -1,454 +0,0 @@ -# Unicode {#unicode} - -With a little help from your OS, practically any Unicode character can be input using your keyboard. - -## Caveats {#caveats} - -There are some limitations to this feature. Because there is no "standard" method of Unicode input across all operating systems, each of them require their own setup process on both the host *and* in the firmware, which may involve installation of additional software. This also means Unicode input will not "just work" when the keyboard is plugged into another device. - -## Usage {#usage} - -The core Unicode API can be used purely programmatically. However, there are also additional subsystems which build on top of it and come with keycodes to make things easier. See below for more details. - -Add the following to your keymap's `rules.mk`: - -```make -UNICODE_COMMON = yes -``` - -## Basic Configuration {#basic-configuration} - -Add the following to your `config.h`: - -|Define |Default |Description | -|------------------------|------------------|--------------------------------------------------------------------------------| -|`UNICODE_KEY_MAC` |`KC_LEFT_ALT` |The key to hold when beginning a Unicode sequence with the macOS input mode | -|`UNICODE_KEY_LNX` |`LCTL(LSFT(KC_U))`|The key to tap when beginning a Unicode sequence with the Linux input mode | -|`UNICODE_KEY_WINC` |`KC_RIGHT_ALT` |The key to hold when beginning a Unicode sequence with the WinCompose input mode| -|`UNICODE_SELECTED_MODES`|`-1` |A comma separated list of input modes for cycling through | -|`UNICODE_CYCLE_PERSIST` |`true` |Whether to persist the current Unicode input mode to EEPROM | -|`UNICODE_TYPE_DELAY` |`10` |The amount of time to wait, in milliseconds, between Unicode sequence keystrokes| - -### Audio Feedback {#audio-feedback} - -If you have the [Audio](feature_audio) feature enabled on your board, you can configure it to play sounds when the input mode is changed. - -Add the following to your `config.h`: - -|Define |Default|Description | -|-------------------|-------|-----------------------------------------------------------| -|`UNICODE_SONG_MAC` |*n/a* |The song to play when the macOS input mode is selected | -|`UNICODE_SONG_LNX` |*n/a* |The song to play when the Linux input mode is selected | -|`UNICODE_SONG_BSD` |*n/a* |The song to play when the BSD input mode is selected | -|`UNICODE_SONG_WIN` |*n/a* |The song to play when the Windows input mode is selected | -|`UNICODE_SONG_WINC`|*n/a* |The song to play when the WinCompose input mode is selected| - -## Input Subsystems {#input-subsystems} - -Each of these subsystems have their own pros and cons in terms of flexibility and ease of use. Choose the one that best fits your needs. - -::::tabs - -=== Basic - -This is the easiest to use, albeit somewhat limited. It supports code points up to `U+7FFF`, which covers characters for most modern languages (including East Asian), as well as many symbols, but does not include emoji. - -To enable Basic Unicode, add the following to your `rules.mk`: - -```make -UNICODE_ENABLE = yes -``` - -You can then add `UC(c)` keycodes to your keymap, where *c* is the code point of the desired character (in hexadecimal - the `U+` prefix will not work). For example, `UC(0x40B)` will output [Ћ](https://unicode-table.com/en/040B/), and `UC(0x30C4)` will output [ツ](https://unicode-table.com/en/30C4). - -=== Unicode Map - -Unicode Map supports all possible code points (up to `U+10FFFF`). Here, the code points are stored in a separate mapping table (which may contain at most 16,384 entries), instead of directly in the keymap. - -To enable Unicode Map, add the following to your `rules.mk`: - -```make -UNICODEMAP_ENABLE = yes -``` - -Then, you will need to create a mapping table in your `keymap.c`, and (optionally) an enum for naming the array indices, like so: - -```c -enum unicode_names { - BANG, - IRONY, - SNEK -}; - -const uint32_t PROGMEM unicode_map[] = { - [BANG] = 0x203D, // ‽ - [IRONY] = 0x2E2E, // ⸮ - [SNEK] = 0x1F40D, // 🐍 -}; -``` - -Finally, add `UM(i)` keycodes to your keymap, where *i* is an index into the `unicode_map[]` array. If you defined the enum above, you can use those names instead, for example `UM(BANG)` or `UM(SNEK)`. - -#### Lower and Upper Case Pairs {#unicodemap-pairs} - -Some writing systems have lowercase and uppercase variants of each character, such as å and Å. To make inputting these characters easier, you can use the `UP(i, j)` keycode in your keymap, where *i* and *j* are the mapping table indices of the lowercase and uppercase characters, respectively. If you're holding down Shift or have Caps Lock turned on when you press the key, the uppercase character will be inserted; otherwise, the lowercase character will be inserted. - -```c -const uint32_t PROGMEM unicode_map[] = { - [AE_LOWER] = 0x00E6, // æ - [AE_UPPER] = 0x00C6, // Æ -}; -``` - -This is most useful when creating a keymap for an international layout with special characters. Instead of having to put the lower and upper case versions of a character on separate keys, you can have them both on the same key. This helps blend Unicode keys in with regular keycodes. - -Due to keycode size constraints, *i* and *j* can each only refer to one of the first 128 characters in your `unicode_map`. In other words, 0 ≤ *i* ≤ 127 and 0 ≤ *j* ≤ 127. - -=== UCIS - -As with Unicode Map, the UCIS method also supports all possible code points, and requires the use of a mapping table. However, it works much differently - Unicode characters are input by replacing a typed mnemonic. - -To enable UCIS, add the following to your keymap's `rules.mk`: - -```make -UCIS_ENABLE = yes -``` - -Then, create a mapping table in your `keymap.c`: - -```c -const ucis_symbol_t ucis_symbol_table[] = UCIS_TABLE( - UCIS_SYM("poop", 0x1F4A9), // 💩 - UCIS_SYM("rofl", 0x1F923), // 🤣 - UCIS_SYM("ukr", 0x1F1FA, 0x1F1E6), // 🇺🇦 - UCIS_SYM("look", 0x0CA0, 0x005F, 0x0CA0) // ಠ_ಠ -); -``` - -By default, each table entry may be up to three code points long. This can be changed by adding `#define UCIS_MAX_CODE_POINTS n` to your keymap's `config.h`. - -To invoke UCIS input, the `ucis_start()` function must first be called (for example, in a custom "Unicode" keycode). Then, type the mnemonic for the mapping table entry (such as "rofl"), and hit Space or Enter. The "rofl" text will be backspaced and the emoji inserted. - -:::: - -## Input Modes {#input-modes} - -Unicode input works by typing a sequence of characters, similar to a macro. However, since this sequence depends on your OS, you will need to prepare both your host machine and QMK to recognise and send the correct Unicode input sequences respectively. - -To set the list of enabled input modes, add the `UNICODE_SELECTED_MODES` define to your keymap's `config.h`, for example: - -```c -#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX -// or -#define UNICODE_SELECTED_MODES UNICODE_MODE_MACOS, UNICODE_MODE_WINCOMPOSE -``` - -These modes can then be cycled through using the `UC_NEXT` and `UC_PREV` keycodes. You can also switch to any input mode, even if it is not specified in `UNICODE_SELECTED_MODES`, using their respective keycodes. - -If your keyboard has working EEPROM, it will remember the last used input mode and continue using it on the next power up. This can be disabled by defining `UNICODE_CYCLE_PERSIST` to `false`. - -:::::tabs - -==== macOS - -**Mode Name:** `UNICODE_MODE_MACOS` - -macOS has built-in support for Unicode input as its own input source. It supports all possible code points by way of surrogate pairs for code points above `U+FFFF`. - -To enable, go to **System Preferences → Keyboard → Input Sources**, then add Unicode Hex Input to the list (under Other), and activate it from the input dropdown in the menu bar. Note that this may disable some Option-based shortcuts such as Option+Left and Option+Right. - -==== Linux (IBus) - -**Mode Name:** `UNICODE_MODE_LINUX` - -For Linux distros with IBus, Unicode input is enabled by default, supports all possible code points, and works almost anywhere. Without IBus, it works under GTK apps, but rarely anywhere else. - -Users who would like support in non-GTK apps without IBus may need to resort to a more indirect method, such as creating a custom keyboard layout. - -==== Windows (WinCompose) - -**Mode Name:** `UNICODE_MODE_WINCOMPOSE` - -This mode requires a third-party tool called [WinCompose](https://github.com/samhocevar/wincompose). It supports all possible code points, and is the recommended input mode for Windows. - -To enable, install the [latest release from GitHub](https://github.com/samhocevar/wincompose/releases/latest). Once installed, it will automatically run on startup. This works reliably under all versions of Windows supported by WinCompose. - -==== Windows (HexNumpad) - -**Mode Name:** `UNICODE_MODE_WINDOWS` - -::: warning -This input mode is *not* the "Alt code" system. Alt codes are not Unicode; they instead follow [the Windows-1252 character set](https://en.wikipedia.org/wiki/Alt_code). -::: - -This is Windows' built-in hex numpad Unicode input mode. It only supports code points up to `U+FFFF`, and is not recommended due to reliability and compatibility issues. - -To enable, run the following as an administrator, then reboot: - -``` -reg add "HKCU\Control Panel\Input Method" -v EnableHexNumpad -t REG_SZ -d 1 -``` - -==== Emacs - -**Mode Name:** `UNICODE_MODE_EMACS` - -Emacs supports code point input with the `insert-char` command. - -==== BSD - -**Mode Name:** `UNICODE_MODE_BSD` - -Not currently implemented. If you're a BSD user and want to contribute support for this input mode, please [feel free](contributing)! - -::::: - -## Keycodes {#keycodes} - -|Key |Aliases |Description | -|----------------------------|---------|----------------------------------------------------------------| -|`UC(c)` | |Send Unicode code point `c`, up to `0x7FFF` | -|`UM(i)` | |Send Unicode code point at index `i` in `unicode_map` | -|`UP(i, j)` | |Send Unicode code point at index `i`, or `j` if Shift/Caps is on| -|`QK_UNICODE_MODE_NEXT` |`UC_NEXT`|Cycle through selected input modes | -|`QK_UNICODE_MODE_PREVIOUS` |`UC_PREV`|Cycle through selected input modes in reverse | -|`QK_UNICODE_MODE_MACOS` |`UC_MAC` |Switch to macOS input | -|`QK_UNICODE_MODE_LINUX` |`UC_LINX`|Switch to Linux input | -|`QK_UNICODE_MODE_WINDOWS` |`UC_WIN` |Switch to Windows input | -|`QK_UNICODE_MODE_BSD` |`UC_BSD` |Switch to BSD input (not implemented) | -|`QK_UNICODE_MODE_WINCOMPOSE`|`UC_WINC`|Switch to Windows input using WinCompose | -|`QK_UNICODE_MODE_EMACS` |`UC_EMAC`|Switch to emacs (`C-x-8 RET`) | - -## API {#api} - -### `uint8_t get_unicode_input_mode(void)` {#api-get-unicode-input-mode} - -Get the current Unicode input mode. - -#### Return Value {#api-get-unicode-input-mode-return-value} - -The currently active Unicode input mode. - ---- - -### `void set_unicode_input_mode(uint8_t mode)` {#api-set-unicode-input-mode} - -Set the Unicode input mode. - -#### Arguments {#api-set-unicode-input-mode-arguments} - - - `uint8_t mode` - The input mode to set. - ---- - -### `void unicode_input_mode_step(void)` {#api-unicode-input-mode-step} - -Change to the next Unicode input mode. - ---- - -### `void unicode_input_mode_step_reverse(void)` {#api-unicode-input-mode-step-reverse} - -Change to the previous Unicode input mode. - ---- - -### `void unicode_input_mode_set_user(uint8_t input_mode)` {#api-unicode-input-mode-set-user} - -User-level callback, invoked when the input mode is changed. - -#### Arguments {#api-unicode-input-mode-set-user-arguments} - - - `uint8_t input_mode` - The new input mode. - ---- - -### `void unicode_input_mode_set_kb(uint8_t input_mode)` {#api-unicode-input-mode-set-kb} - -Keyboard-level callback, invoked when the input mode is changed. - -#### Arguments {#api-unicode-input-mode-set-kb-arguments} - - - `uint8_t input_mode` - The new input mode. - ---- - -### `void unicode_input_start(void)` {#api-unicode-input-start} - -Begin the Unicode input sequence. The exact behavior depends on the currently selected input mode: - - - **macOS**: Hold `UNICODE_KEY_MAC` - - **Linux**: Tap `UNICODE_KEY_LNX` - - **WinCompose**: Tap `UNICODE_KEY_WINC`, then U - - **HexNumpad**: Hold Left Alt, then tap Numpad + - - **Emacs**: Tap Ctrl+X, then 8, then Enter - -This function is weakly defined, and can be overridden in user code. - ---- - -### `void unicode_input_finish(void)` {#api-unicode-input-finish} - -Complete the Unicode input sequence. The exact behavior depends on the currently selected input mode: - - - **macOS**: Release `UNICODE_KEY_MAC` - - **Linux**: Tap Space - - **WinCompose**: Tap Enter - - **HexNumpad**: Release Left Alt - - **Emacs**: Tap Enter - -This function is weakly defined, and can be overridden in user code. - ---- - -### `void unicode_input_cancel(void)` {#api-unicode-input-cancel} - -Cancel the Unicode input sequence. The exact behavior depends on the currently selected input mode: - - - **macOS**: Release `UNICODE_KEY_MAC` - - **Linux**: Tap Escape - - **WinCompose**: Tap Escape - - **HexNumpad**: Release Left Alt - - **Emacs**: Tap Ctrl+G - -This function is weakly defined, and can be overridden in user code. - ---- - -### `void register_unicode(uint32_t code_point)` {#api-register-unicode} - -Input a single Unicode character. A surrogate pair will be sent if required by the input mode. - -#### Arguments {#api-register-unicode-arguments} - - - `uint32_t code_point` - The code point of the character to send. - ---- - -### `void send_unicode_string(const char *str)` {#api-send-unicode-string} - -Send a string containing Unicode characters. - -#### Arguments {#api-send-unicode-string-arguments} - - - `const char *str` - The string to send. - ---- - -### `uint8_t unicodemap_index(uint16_t keycode)` {#api-unicodemap-index} - -Get the index into the `unicode_map` array for the given keycode, respecting shift state for pair keycodes. - -#### Arguments {#api-unicodemap-index-arguments} - - - `uint16_t keycode` - The Unicode Map keycode to get the index of. - -#### Return Value {#api-unicodemap-index-return-value} - -An index into the `unicode_map` array. - ---- - -### `uint32_t unicodemap_get_code_point(uint8_t index)` {#api-unicodemap-get-code-point} - -Get the code point for the given index in the `unicode_map` array. - -#### Arguments {#unicodemap-get-code-point-arguments} - - - `uint8_t index` - The index into the `unicode_map` array. - -#### Return Value {#unicodemap-get-code-point-return-value} - -A Unicode code point value. - ---- - -### `void register_unicodemap(uint8_t index)` {#api-register-unicodemap} - -Send the code point for the given index in the `unicode_map` array. - -#### Arguments {#api-register-unicodemap-arguments} - - - `uint8_t index` - The index into the `unicode_map` array. - ---- - -### `void ucis_start(void)` {#api-ucis-start} - -Begin the input sequence. - ---- - -### `bool ucis_active(void)` {#api-ucis-active} - -Whether UCIS is currently active. - -#### Return Value {#api-ucis-active-return-value} - -`true` if UCIS is active. - ---- - -### `uint8_t ucis_count(void)` {#api-ucis-count} - -Get the number of characters in the input sequence buffer. - -#### Return Value {#api-ucis-count-return-value} - -The current input sequence buffer length. - ---- - -### `bool ucis_add(uint16_t keycode)` {#api-ucis-add} - -Add the given keycode to the input sequence buffer. - -#### Arguments {#api-ucis-add-arguments} - - - `uint16_t keycode` - The keycode to add. Must be between `KC_A` and `KC_Z`, or `KC_1` and `KC_0`. - -#### Return Value {#api-ucis-add-return-value} - -`true` if the keycode was added. - ---- - -### `bool ucis_remove_last(void)` {#api-ucis-remove-last} - -Remove the last character from the input sequence buffer. - -#### Return Value {#api-ucis-remove-last-return-value} - -`true` if the sequence was not empty. - ---- - -### `void ucis_finish(void)` {#api-ucis-finish} - -Mark the input sequence as complete, and attempt to match. - ---- - -### `void ucis_cancel(void)` {#api-ucis-cancel} - -Cancel the input sequence. - ---- - -### `void register_ucis(void)` {#api-register-ucis} - -Send the code point(s) for the given UCIS index. - -#### Arguments {#api-register-ucis-arguments} - - - `uint8_t index` - The index into the UCIS symbol table. diff --git a/docs/feature_wpm.md b/docs/feature_wpm.md deleted file mode 100644 index 4f4ed43739..0000000000 --- a/docs/feature_wpm.md +++ /dev/null @@ -1,76 +0,0 @@ -# Word Per Minute (WPM) Calculation - -The WPM feature uses time between keystrokes to compute a rolling average words per minute rate and makes this available for various uses. - -Enable the WPM system by adding this to your `rules.mk`: - - WPM_ENABLE = yes - -For split keyboards using soft serial, the computed WPM score will be available on the master AND slave half. - -## Configuration - -| Define | Default | Description | -|------------------------------|---------------|------------------------------------------------------------------------------------------| -| `WPM_ESTIMATED_WORD_SIZE` | `5` | This is the value used when estimating average word size (for regression and normal use) | -| `WPM_ALLOW_COUNT_REGRESSION` | _Not defined_ | If defined allows the WPM to be decreased when hitting Delete or Backspace | -| `WPM_UNFILTERED` | _Not defined_ | If undefined (the default), WPM values will be smoothed to avoid sudden changes in value | -| `WPM_SAMPLE_SECONDS` | `5` | This defines how many seconds of typing to average, when calculating WPM | -| `WPM_SAMPLE_PERIODS` | `25` | This defines how many sampling periods to use when calculating WPM | -| `WPM_LAUNCH_CONTROL` | _Not defined_ | If defined, WPM values will be calculated using partial buffers when typing begins | - -'WPM_UNFILTERED' is potentially useful if you're filtering data in some other way (and also because it reduces the code required for the WPM feature), or if reducing measurement latency to a minimum is important for you. - -Increasing 'WPM_SAMPLE_SECONDS' will give more smoothly changing WPM values at the expense of slightly more latency to the WPM calculation. - -Increasing 'WPM_SAMPLE_PERIODS' will improve the smoothness at which WPM decays once typing stops, at a cost of approximately this many bytes of firmware space. - -If 'WPM_LAUNCH_CONTROL' is defined, whenever WPM drops to zero, the next time typing begins WPM will be calculated based only on the time since that typing began, instead of the whole period of time specified by WPM_SAMPLE_SECONDS. This results in reaching an accurate WPM value much faster, even when filtering is enabled and a large WPM_SAMPLE_SECONDS value is specified. - -## Public Functions - -|Function |Description | -|--------------------------|--------------------------------------------------| -|`get_current_wpm(void)` | Returns the current WPM as a value between 0-255 | -|`set_current_wpm(x)` | Sets the current WPM to `x` (between 0-255) | - -## Callbacks - -By default, the WPM score only includes letters, numbers, space and some punctuation. If you want to change the set of characters considered as part of the WPM calculation, you can implement your own `bool wpm_keycode_user(uint16_t keycode)` and return true for any characters you would like included in the calculation, or false to not count that particular keycode. - -For instance, the default is: - -```c -bool wpm_keycode_user(uint16_t keycode) { - if ((keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX) || (keycode >= QK_LAYER_TAP && keycode <= QK_LAYER_TAP_MAX) || (keycode >= QK_MODS && keycode <= QK_MODS_MAX)) { - keycode = keycode & 0xFF; - } else if (keycode > 0xFF) { - keycode = 0; - } - if ((keycode >= KC_A && keycode <= KC_0) || (keycode >= KC_TAB && keycode <= KC_SLSH)) { - return true; - } - - return false; -} -``` - -Additionally, if `WPM_ALLOW_COUNT_REGRESSION` is defined, there is the `uint8_t wpm_regress_count(uint16_t keycode)` function that allows you to decrease the WPM. This is useful if you want to be able to penalize certain keycodes (or even combinations). - -```c -__attribute__((weak)) uint8_t wpm_regress_count(uint16_t keycode) { - bool weak_modded = (keycode >= QK_LCTL && keycode < QK_LSFT) || (keycode >= QK_RCTL && keycode < QK_RSFT); - - if ((keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX) || (keycode >= QK_LAYER_TAP && keycode <= QK_LAYER_TAP_MAX) || (keycode >= QK_MODS && keycode <= QK_MODS_MAX)) { - keycode = keycode & 0xFF; - } else if (keycode > 0xFF) { - keycode = 0; - } - if (((get_mods() | get_oneshot_mods()) & MOD_MASK_CTRL} || weak_modded) && (keycode == KC_DEL || keycode == KC_BSPC)) { - return WPM_ESTIMATED_WORD_SIZE; - } - if (keycode == KC_DEL || keycode == KC_BSPC) { - return 1; - } -} -``` diff --git a/docs/features/audio.md b/docs/features/audio.md new file mode 100644 index 0000000000..2f5e5b2d98 --- /dev/null +++ b/docs/features/audio.md @@ -0,0 +1,377 @@ +# Audio + +Your keyboard can make sounds! If you've got a spare pin you can hook up a simple speaker and make it beep. You can use those beeps to indicate layer transitions, modifiers, special keys, or just to play some funky 8bit tunes. + +To activate this feature, add `AUDIO_ENABLE = yes` to your `rules.mk`. + +## AVR based boards +On Atmega32U4 based boards, up to two simultaneous tones can be rendered. +With one speaker connected to a PWM capable pin on PORTC driven by timer 3 and the other on one of the PWM pins on PORTB driven by timer 1. + +The following pins can be configured as audio outputs in `config.h` - for one speaker set either one out of: + +* `#define AUDIO_PIN C4` +* `#define AUDIO_PIN C5` +* `#define AUDIO_PIN C6` +* `#define AUDIO_PIN B5` +* `#define AUDIO_PIN B6` +* `#define AUDIO_PIN B7` + +and *optionally*, for a second speaker, one of: +* `#define AUDIO_PIN_ALT B5` +* `#define AUDIO_PIN_ALT B6` +* `#define AUDIO_PIN_ALT B7` + +### Wiring +per speaker is - for example with a piezo buzzer - the black lead to Ground, and the red lead connected to the selected AUDIO_PIN for the primary; and similarly with AUDIO_PIN_ALT for the secondary. + + +## ARM based boards +for more technical details, see the notes on [Audio driver](../drivers/audio). + + +### DAC (basic) +Most STM32 MCUs have DAC peripherals, with a notable exception of the STM32F1xx series. Generally, the DAC peripheral drives pins A4 or A5. To enable DAC-based audio output on STM32 devices, add `AUDIO_DRIVER = dac_basic` to `rules.mk` and set in `config.h` either: + +`#define AUDIO_PIN A4` or `#define AUDIO_PIN A5` + +the other DAC channel can optionally be used with a secondary speaker, just set: + +`#define AUDIO_PIN_ALT A4` or `#define AUDIO_PIN_ALT A5` + +Do note though that the dac_basic driver is only capable of reproducing one tone per speaker/channel at a time, for more tones simultaneously, try the dac_additive driver. + +#### Wiring: +for two piezos, for example configured as `AUDIO_PIN A4` and `AUDIO_PIN_ALT A5` would be: red lead to A4 and black to Ground, and similarly with the second one: A5 = red, and Ground = black + +another alternative is to drive *one* piezo with both DAC pins - for an extra "push". +wiring red to A4 and black to A5 (or the other way round) and add `#define AUDIO_PIN_ALT_AS_NEGATIVE` to `config.h` + +##### Proton-C Example: +The Proton-C comes (optionally) with one 'builtin' piezo, which is wired to A4+A5. +For this board `config.h` would include these defines: + +```c +#define AUDIO_PIN A5 +#define AUDIO_PIN_ALT A4 +#define AUDIO_PIN_ALT_AS_NEGATIVE +``` + +### DAC (additive) +Another option, besides dac_basic (which produces sound through a square-wave), is to use the DAC to do additive wave synthesis. +With a number of predefined wave-forms or by providing your own implementation to generate samples on the fly. +To use this feature set `AUDIO_DRIVER = dac_additive` in your `rules.mk`, and select in `config.h` EITHER `#define AUDIO_PIN A4` or `#define AUDIO_PIN A5`. + +The used waveform *defaults* to sine, but others can be selected by adding one of the following defines to `config.h`: + +* `#define AUDIO_DAC_SAMPLE_WAVEFORM_SINE` +* `#define AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE` +* `#define AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID` +* `#define AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE` + +Should you rather choose to generate and use your own sample-table with the DAC unit, implement `uint16_t dac_value_generate(void)` with your keyboard - for an example implementation see keyboards/planck/keymaps/synth_sample or keyboards/planck/keymaps/synth_wavetable + + +### PWM (software) +if the DAC pins are unavailable (or the MCU has no usable DAC at all, like STM32F1xx); PWM can be an alternative. +Note that there is currently only one speaker/pin supported. + +set in `rules.mk`: + +`AUDIO_DRIVER = pwm_software` and in `config.h`: +`#define AUDIO_PIN C13` (can be any pin) to have the selected pin output a pwm signal, generated from a timer callback which toggles the pin in software. + +#### Wiring +the usual piezo wiring: red goes to the selected AUDIO_PIN, black goes to ground. + +OR if you can chose to drive one piezo with two pins, for example `#define AUDIO_PIN B1`, `#define AUDIO_PIN_ALT B2` in `config.h`, with `#define AUDIO_PIN_ALT_AS_NEGATIVE` - then the red lead could go to B1, the black to B2. + +### PWM (hardware) +STM32F1xx have to fall back to using PWM, but can do so in hardware; but again on currently only one speaker/pin. + +`AUDIO_DRIVER = pwm_hardware` in `rules.mk`, and in `config.h`: +`#define AUDIO_PIN A8` +`#define AUDIO_PWM_DRIVER PWMD1` +`#define AUDIO_PWM_CHANNEL 1` +(as well as `#define AUDIO_PWM_PAL_MODE 42` if you are on STM32F2 or larger) +which will use Timer 1 to directly drive pin PA8 through the PWM hardware (TIM1_CH1 = PA8). +Should you want to use the pwm-hardware on another pin and timer - be ready to dig into the STM32 data-sheet to pick the right TIMx_CHy and pin-alternate function. + + +## Tone Multiplexing +Since most drivers can only render one tone per speaker at a time (with the one exception: arm dac-additive) there also exists a "workaround-feature" that does time-slicing/multiplexing - which does what the name implies: cycle through a set of active tones (e.g. when playing chords in Music Mode) at a given rate, and put one tone at a time out through the one/few speakers that are available. + +To enable this feature, and configure a starting-rate, add the following defines to `config.h`: +```c +#define AUDIO_ENABLE_TONE_MULTIPLEXING +#define AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT 10 +``` + +The audio core offers interface functions to get/set/change the tone multiplexing rate from within `keymap.c`. + + +## Songs +There's a couple of different sounds that will automatically be enabled without any other configuration: +``` +STARTUP_SONG // plays when the keyboard starts up (audio.c) +GOODBYE_SONG // plays when you press the QK_BOOT key (quantum.c) +AG_NORM_SONG // plays when you press AG_NORM (quantum.c) +AG_SWAP_SONG // plays when you press AG_SWAP (quantum.c) +CG_NORM_SONG // plays when you press CG_NORM (quantum.c) +CG_SWAP_SONG // plays when you press CG_SWAP (quantum.c) +MUSIC_ON_SONG // plays when music mode is activated (process_music.c) +MUSIC_OFF_SONG // plays when music mode is deactivated (process_music.c) +CHROMATIC_SONG // plays when the chromatic music mode is selected (process_music.c) +GUITAR_SONG // plays when the guitar music mode is selected (process_music.c) +VIOLIN_SONG // plays when the violin music mode is selected (process_music.c) +MAJOR_SONG // plays when the major music mode is selected (process_music.c) +``` + +You can override the default songs by doing something like this in your `config.h`: + +```c +#ifdef AUDIO_ENABLE +# define STARTUP_SONG SONG(STARTUP_SOUND) +#endif +``` + +A full list of sounds can be found in [quantum/audio/song_list.h](https://github.com/qmk/qmk_firmware/blob/master/quantum/audio/song_list.h) - feel free to add your own to this list! All available notes can be seen in [quantum/audio/musical_notes.h](https://github.com/qmk/qmk_firmware/blob/master/quantum/audio/musical_notes.h). + +Additionally, if you with to maintain your own list of songs (such as ones that may be copyrighted) and not have them added to the repo, you can create a `user_song_list.h` file and place it in your keymap (or userspace) folder. This file will be automatically included, it just needs to exist. + +To play a custom sound at a particular time, you can define a song like this (near the top of the file): + +```c +float my_song[][2] = SONG(QWERTY_SOUND); +``` + +And then play your song like this: + +```c +PLAY_SONG(my_song); +``` + +Alternatively, you can play it in a loop like this: + +```c +PLAY_LOOP(my_song); +``` + +It's advised that you wrap all audio features in `#ifdef AUDIO_ENABLE` / `#endif` to avoid causing problems when audio isn't built into the keyboard. + +The available keycodes for audio are: + +|Key |Aliases |Description | +|-------------------------|---------|-------------------------------------------| +|`QK_AUDIO_ON` |`AU_ON` |Turns on Audio Feature | +|`QK_AUDIO_OFF` |`AU_OFF` |Turns off Audio Feature | +|`QK_AUDIO_TOGGLE` |`AU_TOGG`|Toggles Audio state | + +::: warning +These keycodes turn all of the audio functionality on and off. Turning it off means that audio feedback, audio clicky, music mode, etc. are disabled, completely. +::: + +## Audio Config + +| Settings | Default | Description | +|----------------------------------|----------------------|---------------------------------------------------------------------------------------------| +|`AUDIO_PIN` | *Not defined* |Configures the pin that the speaker is connected to. | +|`AUDIO_PIN_ALT` | *Not defined* |Configures the pin for a second speaker or second pin connected to one speaker. | +|`AUDIO_PIN_ALT_AS_NEGATIVE` | *Not defined* |Enables support for one speaker connected to two pins. | +|`AUDIO_INIT_DELAY` | *Not defined* |Enables delay during startup song to accomidate for USB startup issues. | +|`AUDIO_ENABLE_TONE_MULTIPLEXING` | *Not defined* |Enables time splicing/multiplexing to create multiple tones simutaneously. | +|`AUDIO_POWER_CONTROL_PIN` | *Not defined* |Enables power control code to enable or cut off power to speaker (such as with PAM8302 amp). | +|`AUDIO_POWER_CONTROL_PIN_ON_STATE`| `1` |The state of the audio power control pin when audio is "on" - `1` for high, `0` for low. | +|`STARTUP_SONG` | `STARTUP_SOUND` |Plays when the keyboard starts up (audio.c) | +|`GOODBYE_SONG` | `GOODBYE_SOUND` |Plays when you press the QK_BOOT key (quantum.c) | +|`AG_NORM_SONG` | `AG_NORM_SOUND` |Plays when you press AG_NORM (process_magic.c) | +|`AG_SWAP_SONG` | `AG_SWAP_SOUND` |Plays when you press AG_SWAP (process_magic.c) | +|`CG_NORM_SONG` | `AG_NORM_SOUND` |Plays when you press CG_NORM (process_magic.c) | +|`CG_SWAP_SONG` | `AG_SWAP_SOUND` |Plays when you press CG_SWAP (process_magic.c) | +|`MUSIC_ON_SONG` | `MUSIC_ON_SOUND` |Plays when music mode is activated (process_music.c) | +|`MUSIC_OFF_SONG` | `MUSIC_OFF_SOUND` |Plays when music mode is deactivated (process_music.c) | +|`MIDI_ON_SONG` | `MUSIC_ON_SOUND` |Plays when midi mode is activated (process_music.c) | +|`MIDI_OFF_SONG` | `MUSIC_OFF_SOUND` |Plays when midi mode is deactivated (process_music.c) | +|`CHROMATIC_SONG` | `CHROMATIC_SOUND` |Plays when the chromatic music mode is selected (process_music.c) | +|`GUITAR_SONG` | `GUITAR_SOUND` |Plays when the guitar music mode is selected (process_music.c) | +|`VIOLIN_SONG` | `VIOLIN_SOUND` |Plays when the violin music mode is selected (process_music.c) | +|`MAJOR_SONG` | `MAJOR_SOUND` |Plays when the major music mode is selected (process_music.c) | +|`DEFAULT_LAYER_SONGS` | *Not defined* |Plays song when switched default layers with [`set_single_persistent_default_layer(layer)`](../ref_functions#setting-the-persistent-default-layer)(quantum.c). | +|`SENDSTRING_BELL` | *Not defined* |Plays chime when the "enter" ("\a") character is sent (send_string.c) | + +## Tempo +the 'speed' at which SONGs are played is dictated by the set Tempo, which is measured in beats-per-minute. Note lengths are defined relative to that. +The initial/default tempo is set to 120 bpm, but can be configured by setting `TEMPO_DEFAULT` in `config.c`. +There is also a set of functions to modify the tempo from within the user/keymap code: +```c +void audio_set_tempo(uint8_t tempo); +void audio_increase_tempo(uint8_t tempo_change); +void audio_decrease_tempo(uint8_t tempo_change); +``` + +## ARM Audio Volume + +For ARM devices, you can adjust the DAC sample values. If your board is too loud for you or your coworkers, you can set the max using `AUDIO_DAC_SAMPLE_MAX` in your `config.h`: + +```c +#define AUDIO_DAC_SAMPLE_MAX 4095U +``` +the DAC usually runs in 12Bit mode, hence a volume of 100% = 4095U + +Note: this only adjusts the volume aka 'works' if you stick to WAVEFORM_SQUARE, since its samples are generated on the fly - any other waveform uses a hardcoded/precomputed sample-buffer. + +## Voices +Aka "audio effects", different ones can be enabled by setting in `config.h` these defines: +`#define AUDIO_VOICES` to enable the feature, and `#define AUDIO_VOICE_DEFAULT something` to select a specific effect +for details see quantum/audio/voices.h and .c + +Keycodes available: + +|Key |Aliases |Description | +|-------------------------|---------|-------------------------------------------| +|`QK_AUDIO_VOICE_NEXT` |`AU_NEXT`|Cycles through the audio voices | +|`QK_AUDIO_VOICE_PREVIOUS`|`AU_PREV`|Cycles through the audio voices in reverse | + +## Music Mode + +The music mode maps your columns to a chromatic scale, and your rows to octaves. This works best with ortholinear keyboards, but can be made to work with others. All keycodes less than `0xFF` get blocked, so you won't type while playing notes - if you have special keys/mods, those will still work. A work-around for this is to jump to a different layer with KC_NOs before (or after) enabling music mode. + +Recording is experimental due to some memory issues - if you experience some weird behavior, unplugging/replugging your keyboard will fix things. + +Keycodes available: + +|Key |Aliases |Description | +|-------------------------|---------|-------------------------------------------| +|`QK_MUSIC_ON` |`MU_ON` |Turns on Music Mode | +|`QK_MUSIC_OFF` |`MU_OFF` |Turns off Music Mode | +|`QK_MUSIC_TOGGLE` |`MU_TOGG`|Toggles Music Mode | +|`QK_MUSIC_MODE_NEXT` |`MU_NEXT`|Cycles through the music modes | + +Available Modes: + * `CHROMATIC_MODE` - Chromatic scale, row changes the octave + * `GUITAR_MODE` - Chromatic scale, but the row changes the string (+5 st) + * `VIOLIN_MODE` - Chromatic scale, but the row changes the string (+7 st) + * `MAJOR_MODE` - Major scale + +In music mode, the following keycodes work differently, and don't pass through: + +* `LCTL` - start a recording +* `LALT` - stop recording/stop playing +* `LGUI` - play recording +* `KC_UP` - speed-up playback +* `KC_DOWN` - slow-down playback + +The pitch standard (`PITCH_STANDARD_A`) is 440.0f by default - to change this, add something like this to your `config.h`: + +```c +#define PITCH_STANDARD_A 432.0f +``` + +You can completely disable Music Mode as well. This is useful, if you're pressed for space on your controller. To disable it, add this to your `config.h`: + +```c +#define NO_MUSIC_MODE +``` + +### Music Mask + +By default, `MUSIC_MASK` is set to `keycode < 0xFF` which means keycodes less than `0xFF` are turned into notes, and don't output anything. You can change this by defining this in your `config.h` like this: + +```c +#define MUSIC_MASK keycode != KC_NO +``` + +Which will capture all keycodes - be careful, this will get you stuck in music mode until you restart your keyboard! + +For a more advanced way to control which keycodes should still be processed, you can use `music_mask_kb(keycode)` in `.c` and `music_mask_user(keycode)` in your `keymap.c`: + +```c + bool music_mask_user(uint16_t keycode) { + switch (keycode) { + case RAISE: + case LOWER: + return false; + default: + return true; + } + } +``` + +Things that return false are not part of the mask, and are always processed. + +### Music Map + +By default, the Music Mode uses the columns and row to determine the scale for the keys. For a board that uses a rectangular matrix that matches the keyboard layout, this is just fine. However, for boards that use a more complicated matrix (such as the Planck Rev6, or many split keyboards) this would result in a very skewed experience. + +However, the Music Map option allows you to remap the scaling for the music mode, so it fits the layout, and is more natural. + +To enable this feature, add `#define MUSIC_MAP` to your `config.h` file, and then you will want to add a `uint8_t music_map` to your keyboard's `c` file, or your `keymap.c`. + +```c +const uint8_t music_map[MATRIX_ROWS][MATRIX_COLS] = LAYOUT_ortho_4x12( + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 +); +``` + +You will want to use whichever `LAYOUT` macro that your keyboard uses here. This maps it to the correct key location. Start in the bottom left of the keyboard layout, and move to the right, and then upwards. Fill in all the entries until you have a complete matrix. + +You can look at the [Planck Keyboard](https://github.com/qmk/qmk_firmware/blob/e9ace1487887c1f8b4a7e8e6d87c322988bec9ce/keyboards/planck/planck.c#L24-L29) as an example of how to implement this. + +## Audio Click + +This adds a click sound each time you hit a button, to simulate click sounds from the keyboard. And the sounds are slightly different for each keypress, so it doesn't sound like a single long note, if you type rapidly. + +Keycodes available: + +|Key |Aliases |Description | +|-------------------------|---------|-------------------------------------------| +|`QK_AUDIO_CLICKY_TOGGLE` |`CK_TOGG`|Toggles Audio clicky mode | +|`QK_AUDIO_CLICKY_ON` |`CK_ON` |Turns on Audio clicky mode | +|`QK_AUDIO_CLICKY_OFF` |`CK_OFF` |Turns on Audio clicky mode | +|`QK_AUDIO_CLICKY_UP` |`CK_UP` |Increases frequency of the clicks | +|`QK_AUDIO_CLICKY_DOWN` |`CK_DOWN`|Decreases frequency of the clicks | +|`QK_AUDIO_CLICKY_RESET` |`CK_RST` |Resets frequency to default | + +The feature is disabled by default, to save space. To enable it, add this to your `config.h`: + +```c +#define AUDIO_CLICKY +``` + +You can configure the default, min and max frequencies, the stepping and built in randomness by defining these values: + +| Option | Default Value | Description | +|--------|---------------|-------------| +| `AUDIO_CLICKY_FREQ_DEFAULT` | 440.0f | Sets the default/starting audio frequency for the clicky sounds. | +| `AUDIO_CLICKY_FREQ_MIN` | 65.0f | Sets the lowest frequency (under 60f are a bit buggy). | +| `AUDIO_CLICKY_FREQ_MAX` | 1500.0f | Sets the highest frequency. Too high may result in coworkers attacking you. | +| `AUDIO_CLICKY_FREQ_FACTOR` | 1.18921f| Sets the stepping of UP/DOWN key codes. This is a multiplicative factor. The default steps the frequency up/down by a musical minor third. | +| `AUDIO_CLICKY_FREQ_RANDOMNESS` | 0.05f | Sets a factor of randomness for the clicks, Setting this to `0f` will make each click identical, and `1.0f` will make this sound much like the 90's computer screen scrolling/typing effect. | +| `AUDIO_CLICKY_DELAY_DURATION` | 1 | An integer note duration where 1 is 1/16th of the tempo, or a sixty-fourth note (see `quantum/audio/musical_notes.h` for implementation details). The main clicky effect will be delayed by this duration. Adjusting this to values around 6-12 will help compensate for loud switches. | + +## MIDI Functionality + +See [MIDI](midi) + +## Audio Keycodes + +|Key |Aliases |Description | +|-------------------------|---------|-------------------------------------------| +|`QK_AUDIO_ON` |`AU_ON` |Turns on Audio Feature | +|`QK_AUDIO_OFF` |`AU_OFF` |Turns off Audio Feature | +|`QK_AUDIO_TOGGLE` |`AU_TOGG`|Toggles Audio state | +|`QK_AUDIO_CLICKY_TOGGLE` |`CK_TOGG`|Toggles Audio clicky mode | +|`QK_AUDIO_CLICKY_ON` |`CK_ON` |Turns on Audio clicky mode | +|`QK_AUDIO_CLICKY_OFF` |`CK_OFF` |Turns on Audio clicky mode | +|`QK_AUDIO_CLICKY_UP` |`CK_UP` |Increases frequency of the clicks | +|`QK_AUDIO_CLICKY_DOWN` |`CK_DOWN`|Decreases frequency of the clicks | +|`QK_AUDIO_CLICKY_RESET` |`CK_RST` |Resets frequency to default | +|`QK_MUSIC_ON` |`MU_ON` |Turns on Music Mode | +|`QK_MUSIC_OFF` |`MU_OFF` |Turns off Music Mode | +|`QK_MUSIC_TOGGLE` |`MU_TOGG`|Toggles Music Mode | +|`QK_MUSIC_MODE_NEXT` |`MU_NEXT`|Cycles through the music modes | +|`QK_AUDIO_VOICE_NEXT` |`AU_NEXT`|Cycles through the audio voices | +|`QK_AUDIO_VOICE_PREVIOUS`|`AU_PREV`|Cycles through the audio voices in reverse | diff --git a/docs/features/auto_shift.md b/docs/features/auto_shift.md new file mode 100644 index 0000000000..45b9048f03 --- /dev/null +++ b/docs/features/auto_shift.md @@ -0,0 +1,398 @@ +# Auto Shift: Why Do We Need a Shift Key? + +Tap a key and you get its character. Tap a key, but hold it *slightly* longer +and you get its shifted state. Voilà! No shift key needed! + +## Why Auto Shift? + +Many people suffer from various forms of RSI. A common cause is stretching your +fingers repetitively long distances. For us on the keyboard, the pinky does that +all too often when reaching for the shift key. Auto Shift looks to alleviate that +problem. + +## How Does It Work? + +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 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). + +There are also the `get_auto_shift_repeat` and `get_auto_shift_no_auto_repeat` +functions for more granular control. Neither will have an effect unless +`AUTO_SHIFT_REPEAT_PER_KEY` or `AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY` respectively +are defined. + +## Are There Limitations to Auto Shift? + +Yes, unfortunately. + +1. 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. +2. 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. +3. Auto Shift does not apply to Tap Hold keys. For automatic shifting of Tap Hold + keys see [Retro Shift](#retro-shift). + +## How Do I Enable Auto Shift? + +Add to your `rules.mk` in the keymap folder: + +``` +AUTO_SHIFT_ENABLE = yes +``` + +If no `rules.mk` exists, you can create one. + +Then compile and install your new firmware with Auto Key enabled! That's it! + +## Modifiers + +By default, Auto Shift is disabled for any key press that is accompanied by one or more +modifiers. Thus, Ctrl+A that you hold for a really long time is not the same +as Ctrl+Shift+A. + +You can re-enable Auto Shift for modifiers by adding a define to your `config.h` + +```c +#define AUTO_SHIFT_MODIFIERS +``` + +In which case, Ctrl+A held past the `AUTO_SHIFT_TIMEOUT` will be sent as Ctrl+Shift+A + + +## Configuring Auto Shift + +If desired, there is some configuration that can be done to change the +behavior of Auto Shift. This is done by setting various variables the +`config.h` file located in your keymap folder. If no `config.h` file exists, you can create one. + +A sample is + +```c +#pragma once + +#define AUTO_SHIFT_TIMEOUT 150 +#define NO_AUTO_SHIFT_SPECIAL +``` + +### AUTO_SHIFT_TIMEOUT (Value in ms) + +This controls how long you have to hold a key before you get the shifted state. +Obviously, this is different for everyone. For the common person, a setting of +135 to 150 works great. However, one should start with a value of at least 175, which +is the default value. Then work down from there. The idea is to have the shortest time required to get the shifted state without having false positives. + +Play with this value until things are perfect. Many find that all will work well +at a given value, but one or two keys will still emit the shifted state on +occasion. This is simply due to habit and holding some keys a little longer +than others. Once you find this value, work on tapping your problem keys a little +quicker than normal and you will be set. + +::: tip +Auto Shift has three special keys that can help you get this value right very quick. See "Auto Shift Setup" for more details! +::: + +For more granular control of this feature, you can add the following to your `config.h`: + +```c +#define AUTO_SHIFT_TIMEOUT_PER_KEY +``` + +You can then add the following function to your keymap: + +```c +uint16_t get_autoshift_timeout(uint16_t keycode, keyrecord_t *record) { + switch(keycode) { + case AUTO_SHIFT_NUMERIC: + return 2 * get_generic_autoshift_timeout(); + case AUTO_SHIFT_SPECIAL: + return get_generic_autoshift_timeout() + 50; + case AUTO_SHIFT_ALPHA: + default: + return get_generic_autoshift_timeout(); + } +} +``` + +Note that you cannot override individual keys that are in one of those groups +if you are using them; trying to add a case for `KC_A` in the above example will +not compile as `AUTO_SHIFT_ALPHA` is there. A possible solution is a second switch +above to handle individual keys with no default case and only referencing the +groups in the below fallback switch. + +### NO_AUTO_SHIFT_SPECIAL (simple define) + +Do not Auto Shift special keys, which include -\_, =+, [{, ]}, ;:, '", ,<, .>, +/?, and the KC_TAB. + +### NO_AUTO_SHIFT_TAB (simple define) + +Do not Auto Shift KC_TAB but leave Auto Shift enabled for the other special +characters. + +### NO_AUTO_SHIFT_SYMBOLS (simple define) + +Do not Auto Shift symbol keys, which include -\_, =+, [{, ]}, ;:, '", ,<, .>, +and /?. + +### NO_AUTO_SHIFT_NUMERIC (simple define) + +Do not Auto Shift numeric keys, zero through nine. + +### NO_AUTO_SHIFT_ALPHA (simple define) + +Do not Auto Shift alpha characters, which include A through Z. + +### AUTO_SHIFT_ENTER (simple define) + +Auto Shift the enter key. + +### Auto Shift Per Key + +There are functions that allows you to determine which keys should be autoshifted, much like the tap-hold keys. + +The first of these, used to simply add a key to Auto Shift, is `get_custom_auto_shifted_key`: + +```c +bool get_custom_auto_shifted_key(uint16_t keycode, keyrecord_t *record) { + switch(keycode) { + case KC_DOT: + return true; + default: + return false; + } +} +``` + +For more granular control, there is `get_auto_shifted_key`. The default function looks like this: + +```c +bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { +# ifndef NO_AUTO_SHIFT_ALPHA + case AUTO_SHIFT_ALPHA: +# endif +# ifndef NO_AUTO_SHIFT_NUMERIC + case AUTO_SHIFT_NUMERIC: +# endif +# ifndef NO_AUTO_SHIFT_SPECIAL +# ifndef NO_AUTO_SHIFT_TAB + case KC_TAB: +# endif +# ifndef NO_AUTO_SHIFT_SYMBOLS + case AUTO_SHIFT_SYMBOLS: +# endif +# endif +# ifdef AUTO_SHIFT_ENTER + case KC_ENT: +# endif + return true; + } + return get_custom_auto_shifted_key(keycode, record); +} +``` + +This functionality is enabled by default, and does not need a define. + +### AUTO_SHIFT_REPEAT (simple define) + +Enables keyrepeat. + +### AUTO_SHIFT_NO_AUTO_REPEAT (simple define) + +Disables automatically keyrepeating when `AUTO_SHIFT_TIMEOUT` is exceeded. + + +### AUTO_SHIFT_ALPHA (predefined key group) + +A predefined group of keys representing A through Z. + +### AUTO_SHIFT_NUMERIC (predefined key group) + +A predefined group of keys representing 0 through 9. Note, these are defined as +1 through 0 since that is the order they normally appear in. + +### AUTO_SHIFT_SYMBOLS (predefined key group) + +A predefined group of keys representing symbolic characters which include -\_, =+, [{, ]}, ;:, '", ,<, .>, +and /?. + +### AUTO_SHIFT_SPECIAL (predefined key group) + +A predefined group of keys that combines AUTO_SHIFT_SYMBOLS and KC_TAB. + +## Custom Shifted Values + +Especially on small keyboards, the default shifted value for many keys is not +optimal. To provide more customizability, there are two user-definable +functions, `autoshift_press/release_user`. These register or unregister the +correct value for the passed key. Below is an example adding period to Auto +Shift and making its shifted value exclamation point. Make sure to use weak +mods - setting real would make any keys following it use their shifted values +as if you were holding the key. Clearing of modifiers is handled by Auto Shift, +and the OS-sent shift value if keyrepeating multiple keys is always that of +the last key pressed (whether or not it's an Auto Shift key). + +You can also have non-shifted keys for the shifted values (or even no shifted +value), just don't set a shift modifier! + +```c +bool get_custom_auto_shifted_key(uint16_t keycode, keyrecord_t *record) { + switch(keycode) { + case KC_DOT: + return true; + default: + return false; + } +} + +void autoshift_press_user(uint16_t keycode, bool shifted, keyrecord_t *record) { + switch(keycode) { + case KC_DOT: + register_code16((!shifted) ? KC_DOT : KC_EXLM); + break; + default: + if (shifted) { + add_weak_mods(MOD_BIT(KC_LSFT)); + } + // & 0xFF gets the Tap key for Tap Holds, required when using Retro Shift + register_code16((IS_RETRO(keycode)) ? keycode & 0xFF : keycode); + } +} + +void autoshift_release_user(uint16_t keycode, bool shifted, keyrecord_t *record) { + switch(keycode) { + case KC_DOT: + unregister_code16((!shifted) ? KC_DOT : KC_EXLM); + break; + default: + // & 0xFF gets the Tap key for Tap Holds, required when using Retro Shift + // The IS_RETRO check isn't really necessary here, always using + // keycode & 0xFF would be fine. + unregister_code16((IS_RETRO(keycode)) ? keycode & 0xFF : keycode); + } +} +``` + +## Retro Shift + +Holding and releasing a Tap Hold key without pressing another key will ordinarily +result in only the hold. With `retro shift` enabled this action will instead +produce a shifted version of the tap keycode on release. + +It does not require [Retro Tapping](../tap_hold#retro-tapping) to be enabled, and +if both are enabled the state of `retro tapping` will only apply if the tap keycode +is not matched by Auto Shift. `RETRO_TAPPING_PER_KEY` and its corresponding +function, however, are checked before `retro shift` is applied. + +To enable `retro shift`, add the following to your `config.h`: + +```c +#define RETRO_SHIFT +``` + +If `RETRO_SHIFT` is defined to a value, hold times greater than that value will +not produce a tap on release for Mod Taps, and instead triggers the hold action. +This enables modifiers to be held for combining with mouse clicks without +generating taps on release. For example: + +```c +#define RETRO_SHIFT 500 +``` + +Without a value set, holds of any length without an interrupting key will produce the shifted value. + +This value (if set) must be greater than one's `TAPPING_TERM`, as the key press +must be designated as a 'hold' by `process_tapping` before we send the modifier. +[Per-key tapping terms](../tap_hold#tapping-term) can be used as a workaround. +There is no such limitation in regards to `AUTO_SHIFT_TIMEOUT` for normal keys. + +**Note:** Tap Holds must be added to Auto Shift, see [here.](auto_shift#auto-shift-per-key) +`IS_RETRO` may be helpful if one wants all Tap Holds retro shifted. + +### Retro Shift and Tap Hold Configurations + +Tap Hold Configurations work a little differently when using Retro Shift. +Referencing `TAPPING_TERM` makes little sense, as holding longer would result in +shifting one of the keys. + +`RETRO_SHIFT` enables [`PERMISSIVE_HOLD`-like behaviour](../tap_hold#permissive-hold) (even if not explicitly enabled) on all mod-taps for which `RETRO_SHIFT` applies. + +## Using Auto Shift Setup + +This will enable you to define three keys temporarily to increase, decrease and report your `AUTO_SHIFT_TIMEOUT`. + +### Setup + +Map three keys temporarily in your keymap: + +|Keycode |Aliases |Description | +|----------------------|---------|--------------------------------------------| +|`QK_AUTO_SHIFT_DOWN` |`AS_DOWN`|Lower the Auto Shift timeout variable (down)| +|`QK_AUTO_SHIFT_UP` |`AS_UP` |Raise the Auto Shift timeout variable (up) | +|`QK_AUTO_SHIFT_REPORT`|`AS_RPT` |Report your current Auto Shift timeout value| +|`QK_AUTO_SHIFT_ON` |`AS_ON` |Turns on the Auto Shift Function | +|`QK_AUTO_SHIFT_OFF` |`AS_OFF` |Turns off the Auto Shift Function | +|`QK_AUTO_SHIFT_TOGGLE`|`AS_TOGG`|Toggles the state of the Auto Shift feature | + +Compile and upload your new firmware. + +### Use + +It is important to note that during these tests, you should be typing +completely normal and with no intention of shifted keys. + +1. Type multiple sentences of alphabetical letters. +2. Observe any upper case letters. +3. If there are none, press the key you have mapped to `AS_DOWN` to decrease + time Auto Shift timeout value and go back to step 1. +4. If there are some upper case letters, decide if you need to work on tapping + those keys with less down time, or if you need to increase the timeout. +5. If you decide to increase the timeout, press the key you have mapped to + `AS_UP` and go back to step 1. +6. Once you are happy with your results, press the key you have mapped to + `AS_RPT`. The keyboard will type by itself the value of your + `AUTO_SHIFT_TIMEOUT`. +7. Update `AUTO_SHIFT_TIMEOUT` in your `config.h` with the value reported. +8. Add `AUTO_SHIFT_NO_SETUP` to your `config.h`. +9. Remove the key bindings `AS_DOWN`, `AS_UP` and `AS_RPT`. +10. Compile and upload your new firmware. + +#### An Example Run + +``` +hello world. my name is john doe. i am a computer programmer playing with +keyboards right now. + +[PRESS AS_DOWN quite a few times] + +heLLo woRLd. mY nAMe is JOHn dOE. i AM A compUTeR proGRaMMER PlAYiNG witH +KEYboArDS RiGHT NOw. + +[PRESS AS_UP a few times] + +hello world. my name is john Doe. i am a computer programmer playing with +keyboarDs right now. + +[PRESS AS_RPT] + +115 +``` + +The keyboard typed `115` which represents your current `AUTO_SHIFT_TIMEOUT` +value. You are now set! Practice on the *D* key a little bit that showed up +in the testing and you'll be golden. diff --git a/docs/features/autocorrect.md b/docs/features/autocorrect.md new file mode 100644 index 0000000000..df3f2e0fd8 --- /dev/null +++ b/docs/features/autocorrect.md @@ -0,0 +1,324 @@ +# Autocorrect + +There are a lot of words that are prone to being typed incorrectly, due to habit, sequence or just user error. This feature leverages your firmware to automatically correct these errors, to help reduce typos. + +## How does it work? {#how-does-it-work} + +The feature maintains a small buffer of recent key presses. On each key press, it checks whether the buffer ends in a recognized typo, and if so, automatically sends keystrokes to correct it. + +The tricky part is how to efficiently check the buffer for typos. We don’t want to spend too much memory or time on storing or searching the typos. A good solution is to represent the typos with a trie data structure. A trie is a tree data structure where each node is a letter, and words are formed by following a path to one of the leaves. + +![An example trie](https://i.imgur.com/HL5DP8H.png) + +Since we search whether the buffer ends in a typo, we store the trie writing in reverse. The trie is queried starting from the last letter, then second to last letter, and so on, until either a letter doesn’t match or we reach a leaf, meaning a typo was found. + +## How do I enable Autocorrection {#how-do-i-enable-autocorrection} + +In your `rules.mk`, add this: + +```make +AUTOCORRECT_ENABLE = yes +``` + +Additionally, you will need a library for autocorrection. A small sample library is included by default, so that you can get up and running right away, but you can provide a customized library. + +By default, autocorrect is disabled. To enable it, you need to use the `AC_TOGG` keycode to enable it. The status is stored in persistent memory, so you shouldn't need to enabled it again. + +## Customizing autocorrect library {#customizing-autocorrect-library} + +To provide a custom library, you need to create a text file with the corrections. For instance: + +```text +:thier -> their +fitler -> filter +lenght -> length +ouput -> output +widht -> width +``` + +The syntax is `typo -> correction`. Typos and corrections are case insensitive, and any whitespace before or after the typo and correction is ignored. The typo must be only the letters a–z, or the special character : representing a word break. The correction may have any non-unicode characters. + +Then, run: + +```sh +qmk generate-autocorrect-data autocorrect_dictionary.txt +``` + +This will process the file and produce an `autocorrect_data.h` file with the trie library, in the folder that you are at. You can specify the keyboard and keymap (eg `-kb planck/rev6 -km jackhumbert`), and it will place the file in that folder instead. But as long as the file is located in your keymap folder, or user folder, it should be picked up automatically. + +This file will look like this: + +```c +// :thier -> their +// fitler -> filter +// lenght -> length +// ouput -> output +// widht -> width + +#define AUTOCORRECT_MIN_LENGTH 5 // "ouput" +#define AUTOCORRECT_MAX_LENGTH 6 // ":thier" + +#define DICTIONARY_SIZE 74 + +static const uint8_t autocorrect_data[DICTIONARY_SIZE] PROGMEM = {85, 7, 0, 23, 35, 0, 0, 8, 0, 76, 16, 0, 15, 25, 0, 0, + 11, 23, 44, 0, 130, 101, 105, 114, 0, 23, 12, 9, 0, 131, 108, 116, 101, 114, 0, 75, 42, 0, 24, 64, 0, 0, 71, 49, 0, + 10, 56, 0, 0, 12, 26, 0, 129, 116, 104, 0, 17, 8, 15, 0, 129, 116, 104, 0, 19, 24, 18, 0, 130, 116, 112, 117, 116, + 0}; +``` + +### Avoiding false triggers {#avoiding-false-triggers} + +By default, typos are searched within words, to find typos within longer identifiers like maxFitlerOuput. While this is useful, a consequence is that autocorrection will falsely trigger when a typo happens to be a substring of a correctly-spelled word. For instance, if we had thier -> their as an entry, it would falsely trigger on (correct, though relatively uncommon) words like “wealthier” and “filthier.” + +The solution is to set a word break : before and/or after the typo to constrain matching. : matches space, period, comma, underscore, digits, and most other non-alpha characters. + +|Text |thier |:thier |thier: |:thier: | +|-----------------|:------:|:------:|:------:|:------:| +|see `thier` typo |matches |matches |matches |matches | +|it’s `thiers` |matches |matches |no |no | +|wealthier words |matches |no |matches |no | + +:thier: is most restrictive, matching only when thier is a whole word. + +The `qmk generate-autocorrect-data` commands can make an effort to check for entries that would false trigger as substrings of correct words. It searches each typo against a dictionary of 25K English words from the english_words Python package, provided it’s installed. (run `python3 -m pip install english_words` to install it.) + +::: tip +Unfortunately, this is limited to just english words, at this point. +::: + +## Overriding Autocorrect + +Occasionally you might actually want to type a typo (for instance, while editing autocorrect_dict.txt) without being autocorrected. There are a couple of ways to do this: + +1. Begin typing the typo. +2. Before typing the last letter, press and release the Ctrl or Alt key. +3. Type the remaining letters. + +This works because the autocorrection implementation doesn’t understand hotkeys, so it resets itself whenever a modifier other than shift is held. + +Additionally, you can use the `AC_TOGG` keycode to toggle the on/off status for Autocorrect. + +### Keycodes {#keycodes} + +|Keycode |Aliases |Description | +|-----------------------|---------|----------------------------------------------| +|`QK_AUTOCORRECT_ON` |`AC_ON` |Turns on the Autocorrect feature. | +|`QK_AUTOCORRECT_OFF` |`AC_OFF` |Turns off the Autocorrect feature. | +|`QK_AUTOCORRECT_TOGGLE`|`AC_TOGG`|Toggles the status of the Autocorrect feature.| + +## User Callback Functions + +### Process Autocorrect + +Callback function `bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods)` is available to customise incoming keycodes and handle exceptions. You can use this function to sanitise input before they are passed onto the autocorrect engine + +::: tip +Sanitisation of input is required because autocorrect will only match 8-bit [basic keycodes](../keycodes_basic) for typos. If valid modifier keys or 16-bit keycodes that are part of a user's word input (such as Shift + A) is passed through, they will fail typo letter detection. For example a [Mod-Tap](../mod_tap) key such as `LCTL_T(KC_A)` is 16-bit and should be masked for the 8-bit `KC_A`. +::: + +The default user callback function is found inside `quantum/process_keycode/process_autocorrect.c`. It covers most use-cases for QMK special functions and quantum keycodes, including [overriding autocorrect](#overriding-autocorrect) with a modifier other than shift. The `process_autocorrect_user` function is `weak` defined to allow user's copy inside `keymap.c` (or code files) to overwrite it. + +#### Process Autocorrect Example + +If you have a custom keycode `QMKBEST` that should be ignored as part of a word, and another custom keycode `QMKLAYER` that should override autocorrect, both can be added to the bottom of the `process_autocorrect_user` `switch` statement in your source code: + +```c +bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods) { + // See quantum_keycodes.h for reference on these matched ranges. + switch (*keycode) { + // Exclude these keycodes from processing. + case KC_LSFT: + case KC_RSFT: + case KC_CAPS: + case QK_TO ... QK_ONE_SHOT_LAYER_MAX: + case QK_LAYER_TAP_TOGGLE ... QK_LAYER_MOD_MAX: + case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX: + return false; + + // Mask for base keycode from shifted keys. + case QK_LSFT ... QK_LSFT + 255: + case QK_RSFT ... QK_RSFT + 255: + if (*keycode >= QK_LSFT && *keycode <= (QK_LSFT + 255)) { + *mods |= MOD_LSFT; + } else { + *mods |= MOD_RSFT; + } + *keycode &= 0xFF; // Get the basic keycode. + return true; +#ifndef NO_ACTION_TAPPING + // Exclude tap-hold keys when they are held down + // and mask for base keycode when they are tapped. + case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: +# ifdef NO_ACTION_LAYER + // Exclude Layer Tap, if layers are disabled + // but action tapping is still enabled. + return false; +# endif + case QK_MOD_TAP ... QK_MOD_TAP_MAX: + // Exclude hold if mods other than Shift is not active + if (!record->tap.count) { + return false; + } + *keycode &= 0xFF; + break; +#else + case QK_MOD_TAP ... QK_MOD_TAP_MAX: + case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: + // Exclude if disabled + return false; +#endif + // Exclude swap hands keys when they are held down + // and mask for base keycode when they are tapped. + case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX: +#ifdef SWAP_HANDS_ENABLE + if (*keycode >= 0x56F0 || !record->tap.count) { + return false; + } + *keycode &= 0xFF; + break; +#else + // Exclude if disabled + return false; +#endif + // Handle custom keycodes + case QMKBEST: + return false; + case QMKLAYER: + *typo_buffer_size = 0; + return false; + } + + // Disable autocorrect while a mod other than shift is active. + if ((*mods & ~MOD_MASK_SHIFT) != 0) { + *typo_buffer_size = 0; + return false; + } + + return true; +} +``` + +::: tip +In this callback function, `return false` will skip processing of that keycode for autocorrect. Adding `*typo_buffer_size = 0` will also reset the autocorrect buffer at the same time, cancelling any current letters already stored in the buffer. +::: + +### Apply Autocorrect + +Additionally, `apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct)` allows for users to add additional handling to the autocorrection, or replace the functionality entirely. This passes on the number of backspaces needed to replace the words, as well as the replacement string (partial word, not the full word), and the typo and corrected strings (complete words). + +::: tip +Due to the way code works (no notion of words, just a stream of letters), the `typo` and `correct` strings are a best bet and could be "wrong". For example you may get `wordtpyo` & `wordtypo` instead of the expected `tpyo` & `typo`. +::: + +#### Apply Autocorrect Example + +This following example will play a sound when a typo is autocorrected and execute the autocorrection itself: + +```c +#ifdef AUDIO_ENABLE +float autocorrect_song[][2] = SONG(TERMINAL_SOUND); +#endif + +bool apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct) { +#ifdef AUDIO_ENABLE + PLAY_SONG(autocorrect_song); +#endif + for (uint8_t i = 0; i < backspaces; ++i) { + tap_code(KC_BSPC); + } + send_string_P(str); + return false; +} +``` + +::: tip +In this callback function, `return false` will stop the normal processing of autocorrect, which requires manually handling of removing the "bad" characters and typing the new characters. +::: + +::: warning +***IMPORTANT***: `str` is a pointer to `PROGMEM` data for the autocorrection. If you return false, and want to send the string, this needs to use `send_string_P` and not `send_string` nor `SEND_STRING`. +::: + +You can also use `apply_autocorrect` to detect and display the event but allow internal code to execute the autocorrection with `return true`: + +```c +bool apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct) { +#ifdef OLED_ENABLE + oled_write_P(PSTR("Auto-corrected"), false); +#endif +#ifdef CONSOLE_ENABLE + printf("'%s' was corrected to '%s'\n", typo, correct); +#endif + return true; +} +``` + +### Autocorrect Status + +Additional user callback functions to manipulate Autocorrect: + +| Function | Description | +|----------------------------|----------------------------------------------| +| `autocorrect_enable()` | Turns Autocorrect on. | +| `autocorrect_disable()` | Turns Autocorrect off. | +| `autocorrect_toggle()` | Toggles Autocorrect. | +| `autocorrect_is_enabled()` | Returns true if Autocorrect is currently on. | + + +## Appendix: Trie binary data format {#appendix} + +This section details how the trie is serialized to byte data in autocorrect_data. You don’t need to care about this to use this autocorrection implementation. But it is documented for the record in case anyone is interested in modifying the implementation, or just curious how it works. + +What I did here is fairly arbitrary, but it is simple to decode and gets the job done. + +### Encoding {#encoding} + +All autocorrection data is stored in a single flat array autocorrect_data. Each trie node is associated with a byte offset into this array, where data for that node is encoded, beginning with root at offset 0. There are three kinds of nodes. The highest two bits of the first byte of the node indicate what kind: + +* 00 ⇒ chain node: a trie node with a single child. +* 01 ⇒ branching node: a trie node with multiple children. +* 10 ⇒ leaf node: a leaf, corresponding to a typo and storing its correction. + +![An example trie](https://i.imgur.com/HL5DP8H.png) + +**Branching node**. Each branch is encoded with one byte for the keycode (KC_A–KC_Z) followed by a link to the child node. Links between nodes are 16-bit byte offsets relative to the beginning of the array, serialized in little endian order. + +All branches are serialized this way, one after another, and terminated with a zero byte. As described above, the node is identified as a branch by setting the two high bits of the first byte to 01, done by bitwise ORing the first keycode with 64. keycode. The root node for the above figure would be serialized like: + +``` ++-------+-------+-------+-------+-------+-------+-------+ +| R|64 | node 2 | T | node 3 | 0 | ++-------+-------+-------+-------+-------+-------+-------+ +``` + +**Chain node**. Tries tend to have long chains of single-child nodes, as seen in the example above with f-i-t-l in fitler. So to save space, we use a different format to encode chains than branching nodes. A chain is encoded as a string of keycodes, beginning with the node closest to the root, and terminated with a zero byte. The child of the last node in the chain is encoded immediately after. That child could be either a branching node or a leaf. + +In the figure above, the f-i-t-l chain is encoded as + +``` ++-------+-------+-------+-------+-------+ +| L | T | I | F | 0 | ++-------+-------+-------+-------+-------+ +``` + +If we were to encode this chain using the same format used for branching nodes, we would encode a 16-bit node link with every node, costing 8 more bytes in this example. Across the whole trie, this adds up. Conveniently, we can point to intermediate points in the chain and interpret the bytes in the same way as before. E.g. starting at the i instead of the l, and the subchain has the same format. + +**Leaf node**. A leaf node corresponds to a particular typo and stores data to correct the typo. The leaf begins with a byte for the number of backspaces to type, and is followed by a null-terminated ASCII string of the replacement text. The idea is, after tapping backspace the indicated number of times, we can simply pass this string to the `send_string_P` function. For fitler, we need to tap backspace 3 times (not 4, because we catch the typo as the final ‘r’ is pressed) and replace it with lter. To identify the node as a leaf, the two high bits are set to 10 by ORing the backspace count with 128: + +``` ++-------+-------+-------+-------+-------+-------+ +| 3|128 | 'l' | 't' | 'e' | 'r' | 0 | ++-------+-------+-------+-------+-------+-------+ +``` + +### Decoding {#decoding} + +This format is by design decodable with fairly simple logic. A 16-bit variable state represents our current position in the trie, initialized with 0 to start at the root node. Then, for each keycode, test the highest two bits in the byte at state to identify the kind of node. + +* 00 ⇒ **chain node**: If the node’s byte matches the keycode, increment state by one to go to the next byte. If the next byte is zero, increment again to go to the following node. +* 01 ⇒ **branching node**: Search the branches for one that matches the keycode, and follow its node link. +* 10 ⇒ **leaf node**: a typo has been found! We read its first byte for the number of backspaces to type, then pass its following bytes to send_string_P to type the correction. + +## Credits + +Credit goes to [getreuer](https://github.com/getreuer) for originally implementing this [here](https://getreuer.info/posts/keyboards/autocorrection/#how-does-it-work). As well as to [filterpaper](https://github.com/filterpaper) for converting the code to use PROGMEM, and additional improvements. diff --git a/docs/features/backlight.md b/docs/features/backlight.md new file mode 100644 index 0000000000..94726756fd --- /dev/null +++ b/docs/features/backlight.md @@ -0,0 +1,309 @@ +# Backlighting {#backlighting} + +Many keyboards support backlit keys by way of individual LEDs placed through or underneath the keyswitches. This feature is distinct from both the [RGB Underglow](rgblight) and [RGB Matrix](rgb_matrix) features as it usually allows for only a single colour per switch, though you can obviously install multiple different single coloured LEDs on a keyboard. + +QMK is able to control the brightness of these LEDs by switching them on and off rapidly in a certain ratio, a technique known as *Pulse Width Modulation*, or PWM. By altering the duty cycle of the PWM signal, it creates the illusion of dimming. + +## Usage {#usage} + +Most keyboards have backlighting enabled by default if they support it, but if it is not working for you (or you have added support), check that your `rules.mk` includes the following: + +```make +BACKLIGHT_ENABLE = yes +``` + +## Keycodes {#keycodes} + +|Key |Aliases |Description | +|-------------------------------|---------|-----------------------------------| +|`QK_BACKLIGHT_TOGGLE` |`BL_TOGG`|Turn the backlight on or off | +|`QK_BACKLIGHT_STEP` |`BL_STEP`|Cycle through backlight levels | +|`QK_BACKLIGHT_ON` |`BL_ON` |Set the backlight to max brightness| +|`QK_BACKLIGHT_OFF` |`BL_OFF` |Turn the backlight off | +|`QK_BACKLIGHT_UP` |`BL_UP` |Increase the backlight level | +|`QK_BACKLIGHT_DOWN` |`BL_DOWN`|Decrease the backlight level | +|`QK_BACKLIGHT_TOGGLE_BREATHING`|`BL_BRTG`|Toggle backlight breathing | + +## Basic Configuration {#basic-configuration} + +Add the following to your `config.h`: + +|Define |Default |Description | +|-----------------------------|------------------|-----------------------------------------------------------------------------------------------------------------| +|`BACKLIGHT_PIN` |*Not defined* |The pin that controls the LEDs | +|`BACKLIGHT_LEVELS` |`3` |The number of brightness levels (maximum 31 excluding off) | +|`BACKLIGHT_CAPS_LOCK` |*Not defined* |Enable Caps Lock indicator using backlight (for keyboards without dedicated LED) | +|`BACKLIGHT_BREATHING` |*Not defined* |Enable backlight breathing, if supported | +|`BREATHING_PERIOD` |`6` |The length of one backlight "breath" in seconds | +|`BACKLIGHT_ON_STATE` |`1` |The state of the backlight pin when the backlight is "on" - `1` for high, `0` for low | +|`BACKLIGHT_LIMIT_VAL` |`255` |The maximum duty cycle of the backlight -- `255` allows for full brightness, any lower will decrease the maximum.| +|`BACKLIGHT_DEFAULT_ON` |`true` |Enable backlight upon clearing the EEPROM | +|`BACKLIGHT_DEFAULT_BREATHING`|`false` |Whether to enable backlight breathing upon clearing the EEPROM | +|`BACKLIGHT_DEFAULT_LEVEL` |`BACKLIGHT_LEVELS`|The default backlight level to use upon clearing the EEPROM | + +Unless you are designing your own keyboard, you generally should not need to change the `BACKLIGHT_PIN` or `BACKLIGHT_ON_STATE`. + +### "On" State {#on-state} + +Most backlight circuits are driven by an N-channel MOSFET or NPN transistor. This means that to turn the transistor *on* and light the LEDs, you must drive the backlight pin, connected to the gate or base, *high*. +Sometimes, however, a P-channel MOSFET, or a PNP transistor is used. In this case, when the transistor is on, the pin is driven *low* instead. + +To configure the "on" state of the backlight circuit, add the following to your `config.h`: + +```c +#define BACKLIGHT_ON_STATE 0 +``` + +### Multiple Backlight Pins {#multiple-backlight-pins} + +Most keyboards have only one backlight pin which controls all backlight LEDs (especially if the backlight is connected to a hardware PWM pin). +The `timer` and `software` drivers allow you to define multiple backlight pins, which will be turned on and off at the same time during the PWM duty cycle. + +This feature allows to set, for instance, the Caps Lock LED's (or any other controllable LED) brightness at the same level as the other LEDs of the backlight. This is useful if you have mapped Control in place of Caps Lock and you need the Caps Lock LED to be part of the backlight instead of being activated when Caps Lock is on, as it is usually wired to a separate pin from the backlight. + +To configure multiple backlight pins, add something like this to your `config.h`, instead of `BACKLIGHT_PIN`: + +```c +#define BACKLIGHT_PINS { F5, B2 } +``` + +## Driver Configuration {#driver-configuration} + +Backlight driver selection is configured in `rules.mk`. Valid drivers are `pwm` (default), `timer`, `software`, or `custom`. See below for information on individual drivers. + +### PWM Driver {#pwm-driver} + +This is the default backlight driver, which leverages the hardware PWM output capability of the microcontroller. + +```make +BACKLIGHT_DRIVER = pwm +``` + +### Timer Driver {#timer-driver} + +This driver is similar to the PWM driver, but instead of directly configuring the pin to output a PWM signal, an interrupt handler is attached to the timer to turn the pin on and off as appropriate. + +```make +BACKLIGHT_DRIVER = timer +``` + +### Software Driver {#software-driver} + +In this mode, PWM is "emulated" while running other keyboard tasks. It offers maximum hardware compatibility without extra platform configuration. However, breathing is not supported, and the backlight can flicker when the keyboard is busy. + +```make +BACKLIGHT_DRIVER = software +``` + +### Custom Driver {#custom-driver} + +If none of the above drivers apply to your board (for example, you are using a separate IC to control the backlight), you can implement a custom backlight driver using a simple API. + +```make +BACKLIGHT_DRIVER = custom +``` + +```c +void backlight_init_ports(void) { + // Optional - runs on startup + // Usually you want to configure pins here +} +void backlight_set(uint8_t level) { + // Optional - runs on level change + // Usually you want to respond to the new value +} + +void backlight_task(void) { + // Optional - runs periodically + // Note that this is called in the main keyboard loop, + // so long running actions here can cause performance issues +} +``` + +## AVR Configuration {#avr-configuration} + +### PWM Driver {#avr-pwm-driver} + +The following table describes the supported pins for the PWM driver. Only cells marked with a timer number are capable of hardware PWM output; any others must use the `timer` driver. + +|Backlight Pin|AT90USB64/128|AT90USB162|ATmega16/32U4|ATmega16/32U2|ATmega32A|ATmega328/P| +|-------------|-------------|----------|-------------|-------------|---------|-----------| +|`B1` | | | | | |Timer 1 | +|`B2` | | | | | |Timer 1 | +|`B5` |Timer 1 | |Timer 1 | | | | +|`B6` |Timer 1 | |Timer 1 | | | | +|`B7` |Timer 1 |Timer 1 |Timer 1 |Timer 1 | | | +|`C4` |Timer 3 | | | | | | +|`C5` |Timer 3 |Timer 1 | |Timer 1 | | | +|`C6` |Timer 3 |Timer 1 |Timer 3 |Timer 1 | | | +|`D4` | | | | |Timer 1 | | +|`D5` | | | | |Timer 1 | | + +### Timer Driver {#avr-timer-driver} + +Any GPIO pin can be used with this driver. The following table describes the supported timers: + +|AT90USB64/128|AT90USB162|ATmega16/32U4|ATmega16/32U2|ATmega32A|ATmega328/P| +|-------------|----------|-------------|-------------|---------|-----------| +|Timers 1 & 3 |Timer 1 |Timers 1 & 3 |Timer 1 |Timer 1 |Timer 1 | + +The following `#define`s apply only to the `timer` driver: + +|Define |Default|Description | +|-----------------------|-------|----------------| +|`BACKLIGHT_PWM_TIMER` |`1` |The timer to use| + +Note that the choice of timer may conflict with the [Audio](audio) feature. + +## ChibiOS/ARM Configuration {#arm-configuration} + +### PWM Driver {#arm-pwm-driver} + +Depending on the ChibiOS board configuration, you may need to enable PWM at the keyboard level. For STM32, this would look like: + +`halconf.h`: +```c +#define HAL_USE_PWM TRUE +``` +`mcuconf.h`: +```c +#undef STM32_PWM_USE_TIM4 +#define STM32_PWM_USE_TIM4 TRUE +``` + +The following `#define`s apply only to the `pwm` driver: + +|Define |Default |Description | +|-----------------------|-------------|---------------------------------------------------------------| +|`BACKLIGHT_PWM_DRIVER` |`PWMD4` |The PWM driver to use | +|`BACKLIGHT_PWM_CHANNEL`|`3` |The PWM channel to use | +|`BACKLIGHT_PAL_MODE` |`2` |The pin alternative function to use | +|`BACKLIGHT_PWM_PERIOD` |*Not defined*|The PWM period in counter ticks - Default is platform dependent| + + +Refer to the ST datasheet for your particular MCU to determine these values. For example, these defaults are set up for pin `B8` on a Proton-C (STM32F303) using `TIM4_CH3` on AF2. Unless you are designing your own keyboard, you generally should not need to change them. + +### Timer Driver {#arm-timer-driver} + +Depending on the ChibiOS board configuration, you may need to enable general-purpose timers at the keyboard level. For STM32, this would look like: + +`halconf.h`: +```c +#define HAL_USE_GPT TRUE +``` +`mcuconf.h`: +```c +#undef STM32_GPT_USE_TIM15 +#define STM32_GPT_USE_TIM15 TRUE +``` + +The following `#define`s apply only to the `timer` driver: + +|Define |Default |Description | +|----------------------|--------|----------------| +|`BACKLIGHT_GPT_DRIVER`|`GPTD15`|The timer to use| + +## Example Schematic + +Since the MCU can only supply so much current to its GPIO pins, instead of powering the backlight directly from the MCU, the backlight pin is connected to a transistor or MOSFET that switches the power to the LEDs. + +In this typical example, the backlight LEDs are all connected in parallel towards an N-channel MOSFET. Its gate pin is wired to one of the microcontroller's GPIO pins through a 470Ω resistor to avoid ringing. +A pulldown resistor is also placed between the gate pin and ground to keep it at a defined state when it is not otherwise being driven by the MCU. +The values of these resistors are not critical - see [this Electronics StackExchange question](https://electronics.stackexchange.com/q/68748) for more information. + +![Backlight example circuit](https://i.imgur.com/BmAvoUC.png) + +## API {#api} + +### `void backlight_toggle(void)` {#api-backlight-toggle} + +Toggle the backlight on or off. + +--- + +### `void backlight_enable(void)` {#api-backlight-enable} + +Turn the backlight on. + +--- + +### `void backlight_disable(void)` {#api-backlight-disable} + +Turn the backlight off. + +--- + +### `void backlight_step(void)` {#api-backlight-step} + +Cycle through backlight levels. + +--- + +### `void backlight_increase(void)` {#api-backlight-increase} + +Increase the backlight level. + +--- + +### `void backlight_decrease(void)` {#api-backlight-decrease} + +Decrease the backlight level. + +--- + +### `void backlight_level(uint8_t level)` {#api-backlight-level} + +Set the backlight level. + +#### Arguments {#api-backlight-level-arguments} + + - `uint8_t level` + The level to set, from 0 to `BACKLIGHT_LEVELS`. + +--- + +### `uint8_t get_backlight_level(void)` {#api-get-backlight-level} + +Get the current backlight level. + +#### Return Value {#api-get-backlight-level-return} + +The current backlight level, from 0 to `BACKLIGHT_LEVELS`. + +--- + +### `bool is_backlight_enabled(void)` {#api-is-backlight-enabled} + +Get the current backlight state. + +#### Return Value {#api-is-backlight-enabled-return} + +`true` if the backlight is enabled. + +--- + +### `void backlight_toggle_breathing(void)` {#api-backlight-toggle-breathing} + +Toggle backlight breathing on or off. + +--- + +### `void backlight_enable_breathing(void)` {#api-backlight-enable-breathing} + +Turn backlight breathing on. + +--- + +### `void backlight_disable_breathing(void)` {#api-backlight-disable-breathing} + +Turn backlight breathing off. + +--- + +### `bool is_backlight_breathing(void)` {#api-is-backlight-breathing} + +Get the current backlight breathing state. + +#### Return Value {#api-is-backlight-breathing-return} + +`true` if backlight breathing is enabled. diff --git a/docs/features/bluetooth.md b/docs/features/bluetooth.md new file mode 100644 index 0000000000..1dbf15f4e1 --- /dev/null +++ b/docs/features/bluetooth.md @@ -0,0 +1,46 @@ +# Bluetooth + +## Bluetooth Known Supported Hardware + +Currently Bluetooth support is limited to AVR based chips. For Bluetooth 2.1, QMK has support for RN-42 modules. For more recent BLE protocols, currently only the Adafruit Bluefruit SPI Friend is directly supported. BLE is needed to connect to iOS devices. Note iOS does not support mouse input. + +|Board |Bluetooth Protocol |Connection Type|rules.mk |Bluetooth Chip| +|----------------------------------------------------------------|--------------------|---------------|---------------------------------|--------------| +|Roving Networks RN-42 (Sparkfun Bluesmirf) |Bluetooth Classic |UART |`BLUETOOTH_DRIVER = rn42` |RN-42 | +|[Bluefruit LE SPI Friend](https://www.adafruit.com/product/2633)|Bluetooth Low Energy|SPI |`BLUETOOTH_DRIVER = bluefruit_le`|nRF51822 | + +Not Supported Yet but possible: +* [Bluefruit LE UART Friend](https://www.adafruit.com/product/2479). [Possible tmk implementation found in](https://github.com/tmk/tmk_keyboard/issues/514) +* HC-05 boards flashed with RN-42 firmware. They apparently both use the CSR BC417 Chip. Flashing it with RN-42 firmware gives it HID capability. +* Sparkfun Bluetooth Mate +* HM-13 based boards + +### Adafruit BLE SPI Friend +Currently The only bluetooth chipset supported by QMK is the Adafruit Bluefruit SPI Friend. It's a Nordic nRF51822 based chip running Adafruit's custom firmware. Data is transmitted via Adafruit's SDEP over Hardware SPI. The [Feather 32u4 Bluefruit LE](https://www.adafruit.com/product/2829) is supported as it's an AVR mcu connected via SPI to the Nordic BLE chip with Adafruit firmware. If Building a custom board with the SPI friend it would be easiest to just use the pin selection that the 32u4 feather uses but you can change the pins in the config.h options with the following defines: +* `#define BLUEFRUIT_LE_RST_PIN D4` +* `#define BLUEFRUIT_LE_CS_PIN B4` +* `#define BLUEFRUIT_LE_IRQ_PIN E6` + +A Bluefruit UART friend can be converted to an SPI friend, however this [requires](https://github.com/qmk/qmk_firmware/issues/2274) some reflashing and soldering directly to the MDBT40 chip. + + +## Bluetooth Rules.mk Options + +The currently supported Bluetooth chipsets do not support [N-Key Rollover (NKRO)](../reference_glossary#n-key-rollover-nkro), so `rules.mk` must contain `NKRO_ENABLE = no`. + +Add the following to your `rules.mk`: + +```make +BLUETOOTH_ENABLE = yes +BLUETOOTH_DRIVER = bluefruit_le # or rn42 +``` + +## Bluetooth Keycodes + +This is used when multiple keyboard outputs can be selected. Currently this only allows for switching between USB and Bluetooth on keyboards that support both. + +|Key |Aliases |Description | +|---------------------|---------|----------------------------------------------| +|`QK_OUTPUT_AUTO` |`OU_AUTO`|Automatically switch between USB and Bluetooth| +|`QK_OUTPUT_USB` |`OU_USB` |USB only | +|`QK_OUTPUT_BLUETOOTH`|`OU_BT` |Bluetooth only | diff --git a/docs/features/bootmagic.md b/docs/features/bootmagic.md new file mode 100644 index 0000000000..49fae5ba45 --- /dev/null +++ b/docs/features/bootmagic.md @@ -0,0 +1,85 @@ +# Bootmagic {#bootmagic} + +The Bootmagic feature that only handles jumping into the bootloader. This is great for boards that don't have a physical reset button, giving you a way to jump into the bootloader + +On some keyboards Bootmagic is disabled by default. If this is the case, it must be explicitly enabled in your `rules.mk` with: + +```make +BOOTMAGIC_ENABLE = yes +``` + +Additionally, you may want to specify which key to use. This is especially useful for keyboards that have unusual matrices. To do so, you need to specify the row and column of the key that you want to use. Add these entries to your `config.h` file: + +```c +#define BOOTMAGIC_ROW 0 +#define BOOTMAGIC_COLUMN 1 +``` + +By default, these are set to 0 and 0, which is usually the "ESC" key on a majority of keyboards. + +And to trigger the bootloader, you hold this key down when plugging the keyboard in. Just the single key. + +::: warning +Using Bootmagic will **always reset** the EEPROM, so you will lose any settings that have been saved. +::: + +## Split Keyboards + +When [handedness](split_keyboard#setting-handedness) is predetermined via options like `SPLIT_HAND_PIN` or `EE_HANDS`, you might need to configure a different key between halves. To identify the correct key for the right half, examine the split key matrix defined in the `.h` file, e.g.: + +```c +#define LAYOUT_split_3x5_2( \ + L01, L02, L03, L04, L05, R01, R02, R03, R04, R05, \ + L06, L07, L08, L09, L10, R06, R07, R08, R09, R10, \ + L11, L12, L13, L14, L15, R11, R12, R13, R14, R15, \ + L16, L17, R16, R17 \ + ) \ + { \ + { L01, L02, L03, L04, L05 }, \ + { L06, L07, L08, L09, L10 }, \ + { L11, L12, L13, L14, L15 }, \ + { L16, L17, KC_NO, KC_NO, KC_NO }, \ + { R01, R02, R03, R04, R05 }, \ + { R06, R07, R08, R09, R10 }, \ + { R11, R12, R13, R14, R15 }, \ + { R16, R17, KC_NO, KC_NO, KC_NO } \ + } +``` + +If you pick the top right key for the right half, it is `R05` on the top layout. Within the key matrix below, `R05` is located on row 4 columnn 4. To use that key as the right half's Bootmagic trigger, add these entries to your `config.h` file: + +```c +#define BOOTMAGIC_ROW_RIGHT 4 +#define BOOTMAGIC_COLUMN_RIGHT 4 +``` + +::: tip +These values are not set by default. +::: + +## Advanced Bootmagic + +The `bootmagic_scan` function is defined weakly, so that you can replace this in your code, if you need. A great example of this is the Zeal60 boards that have some additional handling needed. + +To replace the function, all you need to do is add something like this to your code: + +```c +void bootmagic_scan(void) { + matrix_scan(); + wait_ms(DEBOUNCE * 2); + matrix_scan(); + + if (matrix_get_row(BOOTMAGIC_ROW) & (1 << BOOTMAGIC_COLUMN)) { + // Jump to bootloader. + bootloader_jump(); + } +} +``` + +You can define additional logic here. For instance, resetting the EEPROM or requiring additional keys to be pressed to trigger Bootmagic. Keep in mind that `bootmagic_scan` is called before a majority of features are initialized in the firmware. + +## Addenda + +To manipulate settings that were formerly configured through the now-deprecated full Bootmagic feature, see [Magic Keycodes](../keycodes_magic). + +The Command feature, formerly known as Magic, also allows you to control different aspects of your keyboard. While it shares some functionality with Magic Keycodes, it also allows you to do things that Magic Keycodes cannot, such as printing version information to the console. For more information, see [Command](command). diff --git a/docs/features/caps_word.md b/docs/features/caps_word.md new file mode 100644 index 0000000000..15361aab96 --- /dev/null +++ b/docs/features/caps_word.md @@ -0,0 +1,189 @@ +# Caps Word + +It is often useful to type a single word in all capitals, for instance +abbreviations like "QMK", or in code, identifiers like `KC_SPC`. "Caps Word" is +a modern alternative to Caps Lock: + +* While active, letters are capitalized and `-` becomes `_`. The `_` makes it easier + to type constant names (eg 'PROGRAM\_CONSTANTS'). + +* Caps Word automatically disables + itself at the end of the word. That is, it stops by default once a space or + any key other than `KC_A`--`KC_Z`, `KC_0`--`KC_9`, `KC_MINS`, `KC_UNDS`, + `KC_DELETE`, or `KC_BACKSPACE` is pressed. Caps Word also disables itself if + the keyboard is idle for 5 seconds. This is configurable, see below. + +* To avoid requiring a dedicated key for Caps Word, there is an option + (`BOTH_SHIFTS_TURNS_ON_CAPS_WORD`) to activate Caps Word by simultaneously + pressing both shift keys. See below for other options. + +* The implementation does not use the Caps Lock (`KC_CAPS`) keycode. Caps Word + works even if you're remapping Caps Lock at the OS level to Ctrl or something + else, as Emacs and Vim users often do. As a consequence, Caps Word does not + follow the typical Caps Lock behaviour and may thus act in potentially + unexpected ways, especially when using an *OS* keyboard layout other than US + or UK. For example, Dvorak's , < key (`DV_COMM` aka `KC_W`) will + get shifted because Caps Word interprets that keycode as the letter 'W' by + default, the Spanish Ñ key (`ES_NTIL` aka `KC_SCLN`) will not get + capitalized because Caps Word interprets it as the semicolon ';' punctuation + character, and the US hyphen key (`KC_MINS`), while unaffected by Caps Lock, + is shifted by Caps Word. However, this is not really a problem because you can + [configure which keys should Caps Word + shift](#configure-which-keys-are-word-breaking). + + +## How do I enable Caps Word {#how-do-i-enable-caps-word} + +In your `rules.mk`, add: + +```make +CAPS_WORD_ENABLE = yes +``` + +Next, use one the following methods to activate Caps Word: + +* **Activate by pressing a key**: Use the `QK_CAPS_WORD_TOGGLE` keycode (short + alias `CW_TOGG`) in your keymap. + +* **Activate by pressing Left Shift + Right Shift**: Add `#define + BOTH_SHIFTS_TURNS_ON_CAPS_WORD` to config.h. You may also need to disable or + reconfigure Command, details below. Then, simultaneously pressing both left + and right shifts turns on Caps Word. This method works with the plain + `KC_LSFT` and `KC_RSFT` keycodes as well as one-shot shifts and Space Cadet + shifts. If your shift keys are mod-taps, hold both shift mod-tap keys until + the tapping term, then release them. + +* **Activate by double tapping Left Shift**: Add `#define + DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD` config.h. Then, double tapping Left Shift + turns on Caps Word. This method works with `KC_LSFT` or one-shot Left Shift + `OSM(MOD_LSFT)`. To count as a double tap, the maximum time in milliseconds + between taps is `TAPPING_TERM`, or if using `TAPPING_TERM_PER_KEY`, the time + returned by `get_tapping_term()` for the shift keycode being tapped. + +* **Custom activation**: You can activate Caps Word from code by calling + `caps_word_on()`. This may be used to activate Caps Word through [a + combo](combo) or [tap dance](tap_dance) or any means + you like. + +### Troubleshooting: Command {#troubleshooting-command} + +When using `BOTH_SHIFTS_TURNS_ON_CAPS_WORD`, you might see a compile message +**"BOTH_SHIFTS_TURNS_ON_CAPS_WORD and Command should not be enabled at the same +time, since both use the Left Shift + Right Shift key combination."** + +Many keyboards enable the [Command feature](command), which by +default is also activated using the Left Shift + Right Shift key combination. To +fix this conflict, please disable Command by adding in rules.mk: + +```make +COMMAND_ENABLE = no +``` + +Or configure Command to use another key combination like Left Ctrl + Right Ctrl +by defining `IS_COMMAND()` in config.h: + +```c +// Activate Command with Left Ctrl + Right Ctrl. +#define IS_COMMAND() (get_mods() == MOD_MASK_CTRL) +``` + + +## Customizing Caps Word {#customizing-caps-word} + +### Invert on shift {#invert-on-shift} + +By default, Caps Word turns off when Shift keys are pressed, considering them as +word-breaking. Alternatively with the `CAPS_WORD_INVERT_ON_SHIFT` option, +pressing the Shift key continues Caps Word and inverts the shift state. This +is convenient for uncapitalizing one or a few letters within a word, for +example with Caps Word on, typing "D, B, Shift+A, Shift+A, S" produces "DBaaS", +or typing "P, D, F, Shift+S" produces "PDFs". + +Enable it by adding in config.h + +```c +#define CAPS_WORD_INVERT_ON_SHIFT +``` + +This option works with regular Shift keys `KC_LSFT` and `KC_RSFT`, mod-tap Shift +keys, and one-shot Shift keys. Note that while Caps Word is on, one-shot Shift +keys behave like regular Shift keys, and have effect only while they are held. + + +### Idle timeout {#idle-timeout} + +Caps Word turns off automatically if no keys are pressed for +`CAPS_WORD_IDLE_TIMEOUT` milliseconds. The default is 5000 (5 seconds). +Configure the timeout duration in config.h, for instance + +```c +#define CAPS_WORD_IDLE_TIMEOUT 3000 // 3 seconds. +``` + +Setting `CAPS_WORD_IDLE_TIMEOUT` to 0 configures Caps Word to never time out. +Caps Word then remains active indefinitely until a word breaking key is pressed. + + +### Functions {#functions} + +Functions to manipulate Caps Word: + +| Function | Description | +|-------------------------|------------------------------------------------| +| `caps_word_on()` | Turns Caps Word on. | +| `caps_word_off()` | Turns Caps Word off. | +| `caps_word_toggle()` | Toggles Caps Word. | +| `is_caps_word_on()` | Returns true if Caps Word is currently on. | + + +### Configure which keys are "word breaking" {#configure-which-keys-are-word-breaking} + +You can define the `caps_word_press_user(uint16_t keycode)` callback to +configure which keys should be shifted and which keys are considered "word +breaking" and stop Caps Word. + +The callback is called on every key press while Caps Word is active. When the +key should be shifted (that is, a letter key), the callback should call +`add_weak_mods(MOD_BIT(KC_LSFT))` to shift the key. Returning true continues the +current "word," while returning false is "word breaking" and deactivates Caps +Word. The default callback is + +```c +bool caps_word_press_user(uint16_t keycode) { + switch (keycode) { + // Keycodes that continue Caps Word, with shift applied. + case KC_A ... KC_Z: + case KC_MINS: + add_weak_mods(MOD_BIT(KC_LSFT)); // Apply shift to next key. + return true; + + // Keycodes that continue Caps Word, without shifting. + case KC_1 ... KC_0: + case KC_BSPC: + case KC_DEL: + case KC_UNDS: + return true; + + default: + return false; // Deactivate Caps Word. + } +} +``` + + +### Representing Caps Word state {#representing-caps-word-state} + +Define `caps_word_set_user(bool active)` to get callbacks when Caps Word turns +on or off. This is useful to represent the current Caps Word state, e.g. by +setting an LED or playing a sound. In your keymap, define + +```c +void caps_word_set_user(bool active) { + if (active) { + // Do something when Caps Word activates. + } else { + // Do something when Caps Word deactivates. + } +} +``` + diff --git a/docs/features/combo.md b/docs/features/combo.md new file mode 100644 index 0000000000..bdb8c4b15f --- /dev/null +++ b/docs/features/combo.md @@ -0,0 +1,398 @@ +# Combos + +The Combo feature is a chording type solution for adding custom actions. It lets you hit multiple keys at once and produce a different effect. For instance, hitting `A` and `B` within the combo term would hit `ESC` instead, or have it perform even more complex tasks. + +To enable this feature, you need to add `COMBO_ENABLE = yes` to your `rules.mk`. + +Then, in your `keymap.c` file, you'll need to define a sequence of keys, terminated with `COMBO_END`, and a structure to list the combination of keys, and its resulting action. + +```c +const uint16_t PROGMEM test_combo1[] = {KC_A, KC_B, COMBO_END}; +const uint16_t PROGMEM test_combo2[] = {KC_C, KC_D, COMBO_END}; +combo_t key_combos[] = { + COMBO(test_combo1, KC_ESC), + COMBO(test_combo2, LCTL(KC_Z)), // keycodes with modifiers are possible too! +}; +``` + +This will send "Escape" if you hit the A and B keys, and Ctrl+Z when you hit the C and D keys. + +## Advanced Keycodes Support +Advanced keycodes, such as [Mod-Tap](../mod_tap) and [Tap Dance](tap_dance) are also supported together with combos. If you use these advanced keycodes in your keymap, you will need to place the full keycode in the combo definition, e.g.: + +```c +const uint16_t PROGMEM test_combo1[] = {LSFT_T(KC_A), LT(1, KC_B), COMBO_END}; +const uint16_t PROGMEM test_combo2[] = {TD(TD_ESC_CAPS), KC_F1, COMBO_END}; +``` + +## Overlapping Combos +It is possible to overlap combos. Before, with the example below both combos would activate when all three keys were pressed. Now only the three key combo will activate. + +```c +const uint16_t PROGMEM test_combo1[] = {LSFT_T(KC_A), LT(1, KC_B), COMBO_END}; +const uint16_t PROGMEM test_combo2[] = {LSFT_T(KC_A), LT(1, KC_B), KC_C, COMBO_END}; +combo_t key_combos[] = { + COMBO(test_combo1, KC_ESC) + COMBO(test_combo2, KC_TAB) +}; +``` + +## Examples + +A long list of combos can be defined in an `enum` list: + +```c +enum combos { + AB_ESC, + JK_TAB, + QW_SFT, + SD_LAYER +}; + +const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END}; +const uint16_t PROGMEM jk_combo[] = {KC_J, KC_K, COMBO_END}; +const uint16_t PROGMEM qw_combo[] = {KC_Q, KC_W, COMBO_END}; +const uint16_t PROGMEM sd_combo[] = {KC_S, KC_D, COMBO_END}; + +combo_t key_combos[] = { + [AB_ESC] = COMBO(ab_combo, KC_ESC), + [JK_TAB] = COMBO(jk_combo, KC_TAB), + [QW_SFT] = COMBO(qw_combo, KC_LSFT), + [SD_LAYER] = COMBO(sd_combo, MO(_LAYER)), +}; +``` + +For a more complicated implementation, you can use the `process_combo_event` function to add custom handling. + +```c +enum combo_events { + EM_EMAIL, + BSPC_LSFT_CLEAR, +}; + +const uint16_t PROGMEM email_combo[] = {KC_E, KC_M, COMBO_END}; +const uint16_t PROGMEM clear_line_combo[] = {KC_BSPC, KC_LSFT, COMBO_END}; + +combo_t key_combos[] = { + [EM_EMAIL] = COMBO_ACTION(email_combo), + [BSPC_LSFT_CLEAR] = COMBO_ACTION(clear_line_combo), +}; +/* COMBO_ACTION(x) is same as COMBO(x, KC_NO) */ + +void process_combo_event(uint16_t combo_index, bool pressed) { + switch(combo_index) { + case EM_EMAIL: + if (pressed) { + SEND_STRING("john.doe@example.com"); + } + break; + case BSPC_LSFT_CLEAR: + if (pressed) { + tap_code16(KC_END); + tap_code16(S(KC_HOME)); + tap_code16(KC_BSPC); + } + break; + } +} +``` + +This will send "john.doe@example.com" if you chord E and M together, and clear the current line with Backspace and Left-Shift. You could change this to do stuff like play sounds or change settings. + +It is worth noting that `COMBO_ACTION`s are not needed anymore. As of [PR#8591](https://github.com/qmk/qmk_firmware/pull/8591/), it is possible to run your own custom keycodes from combos. Just define the custom keycode, program its functionality in `process_record_user`, and define a combo with `COMBO(, )`. See the first example in [Macros](../feature_macros). + +## Keycodes +You can enable, disable and toggle the Combo feature on the fly. This is useful if you need to disable them temporarily, such as for a game. The following keycodes are available for use in your `keymap.c` + +|Keycode |Aliases |Description | +|-----------------|---------|--------------------------------| +|`QK_COMBO_ON` |`CM_ON` |Turns on Combo feature | +|`QK_COMBO_OFF` |`CM_OFF` |Turns off Combo feature | +|`QK_COMBO_TOGGLE`|`CM_TOGG`|Toggles Combo feature on and off| + +## Advanced Configuration +These configuration settings can be set in your `config.h` file. + +### Combo Term +By default, the timeout for the Combos to be recognized is set to 50ms. This can be changed if accidental combo misfires are happening or if you're having difficulties pressing keys at the same time. For instance, `#define COMBO_TERM 40` would set the timeout period for combos to 40ms. + +### Buffer and state sizes +If you're using long combos, or you have a lot of overlapping combos, you may run into issues with this, as the buffers may not be large enough to accommodate what you're doing. In this case, you can configure the sizes of the buffers used. Be aware, larger combo sizes and larger buffers will increase memory usage! + +To configure the amount of keys a combo can be composed of, change the following: + +| Keys | Define to be set | +|------|-----------------------------------| +| 6 | `#define EXTRA_SHORT_COMBOS` | +| 8 | QMK Default | +| 16 | `#define EXTRA_LONG_COMBOS` | +| 32 | `#define EXTRA_EXTRA_LONG_COMBOS` | + +Defining `EXTRA_SHORT_COMBOS` combines a combo's internal state into just one byte. This can, in some cases, save some memory. If it doesn't, no point using it. If you do, you also have to make sure you don't define combos with more than 6 keys. + +Processing combos has two buffers, one for the key presses, another for the combos being activated. Use the following options to configure the sizes of these buffers: + +| Define | Default | +|-------------------------------------|------------------------------------------------------| +| `#define COMBO_KEY_BUFFER_LENGTH 8` | 8 (the key amount `(EXTRA_)EXTRA_LONG_COMBOS` gives) | +| `#define COMBO_BUFFER_LENGTH 4` | 4 | + +### Modifier Combos +If a combo resolves to a Modifier, the window for processing the combo can be extended independently from normal combos. By default, this is disabled but can be enabled with `#define COMBO_MUST_HOLD_MODS`, and the time window can be configured with `#define COMBO_HOLD_TERM 150` (default: `TAPPING_TERM`). With `COMBO_MUST_HOLD_MODS`, you cannot tap the combo any more which makes the combo less prone to misfires. + +### Strict key press order +By defining `COMBO_MUST_PRESS_IN_ORDER` combos only activate when the keys are pressed in the same order as they are defined in the key array. + +### Per Combo Timing, Holding, Tapping and Key Press Order +For each combo, it is possible to configure the time window it has to pressed in, if it needs to be held down, if it needs to be tapped, or if its keys need to be pressed in order. + +For example, tap-only combos are useful if any (or all) of the underlying keys are mod-tap or layer-tap keys. When you tap the combo, you get the combo result. When you press the combo and hold it down, the combo doesn't activate. Instead the keys are processed separately as if the combo wasn't even there. + +In order to use these features, the following configuration options and functions need to be defined. Coming up with useful timings and configuration is left as an exercise for the reader. + +| Config Flag | Function | Description | +|-----------------------------|-----------------------------------------------------------|--------------------------------------------------------------------------------------------------------| +| `COMBO_TERM_PER_COMBO` | uint16_t get_combo_term(uint16_t index, combo_t \*combo) | Optional per-combo timeout window. (default: `COMBO_TERM`) | +| `COMBO_MUST_HOLD_PER_COMBO` | bool get_combo_must_hold(uint16_t index, combo_t \*combo) | Controls if a given combo should fire immediately on tap or if it needs to be held. (default: `false`) | +| `COMBO_MUST_TAP_PER_COMBO` | bool get_combo_must_tap(uint16_t index, combo_t \*combo) | Controls if a given combo should fire only if tapped within `COMBO_HOLD_TERM`. (default: `false`) | +| `COMBO_MUST_PRESS_IN_ORDER_PER_COMBO` | bool get_combo_must_press_in_order(uint16_t index, combo_t \*combo) | Controls if a given combo should fire only if its keys are pressed in order. (default: `true`) | + +Examples: +```c +uint16_t get_combo_term(uint16_t index, combo_t *combo) { + // decide by combo->keycode + switch (combo->keycode) { + case KC_X: + return 50; + } + + // or with combo index, i.e. its name from enum. + switch (index) { + case COMBO_NAME_HERE: + return 9001; + } + + // And if you're feeling adventurous, you can even decide by the keys in the chord, + // i.e. the exact array of keys you defined for the combo. + // This can be useful if your combos have a common key and you want to apply the + // same combo term for all of them. + if (combo->keys[0] == KC_ENT) { // if first key in the array is Enter + return 150; + } + + return COMBO_TERM; +} + +bool get_combo_must_hold(uint16_t index, combo_t *combo) { + // Same as above, decide by keycode, the combo index, or by the keys in the chord. + + if (KEYCODE_IS_MOD(combo->keycode) || + (combo->keycode >= QK_MOMENTARY && combo->keycode <= QK_MOMENTARY_MAX) // MO(kc) keycodes + ) { + return true; + } + + switch (index) { + case COMBO_NAME_HERE: + return true; + } + + return false; +} + +bool get_combo_must_tap(uint16_t index, combo_t *combo) { + // If you want all combos to be tap-only, just uncomment the next line + // return true + + // If you want *all* combos, that have Mod-Tap/Layer-Tap/Momentary keys in its chord, to be tap-only, this is for you: + uint16_t key; + uint8_t idx = 0; + while ((key = pgm_read_word(&combo->keys[idx])) != COMBO_END) { + switch (key) { + case QK_MOD_TAP...QK_MOD_TAP_MAX: + case QK_LAYER_TAP...QK_LAYER_TAP_MAX: + case QK_MOMENTARY...QK_MOMENTARY_MAX: + return true; + } + idx += 1; + } + return false; + +} + +bool get_combo_must_press_in_order(uint16_t combo_index, combo_t *combo) { + switch (combo_index) { + /* List combos here that you want to only activate if their keys + * are pressed in the same order as they are defined in the combo's key + * array. */ + case COMBO_NAME_HERE: + return true; + default: + return false; + } +} +``` + +### Generic hook to (dis)allow a combo activation + +By defining `COMBO_SHOULD_TRIGGER` and its companying function `bool combo_should_trigger(uint16_t combo_index, combo_t *combo, uint16_t keycode, keyrecord_t *record)` you can block or allow combos to activate on the conditions of your choice. +For example, you could disallow some combos on the base layer and allow them on another. Or disable combos on the home row when a timer is running. + +Examples: +```c +bool combo_should_trigger(uint16_t combo_index, combo_t *combo, uint16_t keycode, keyrecord_t *record) { + /* Disable combo `SOME_COMBO` on layer `_LAYER_A` */ + switch (combo_index) { + case SOME_COMBO: + if (layer_state_is(_LAYER_A)) { + return false; + } + } + + return true; +} +``` + +### Combo timer + +Normally, the timer is started on the first key press and then reset on every subsequent key press within the `COMBO_TERM`. +Inputting combos is relaxed like this, but also slightly more prone to accidental misfires. + +The next two options alter the behaviour of the timer. + +#### `#define COMBO_STRICT_TIMER` + +With `COMBO_STRICT_TIMER`, the timer is started only on the first key press. +Inputting combos is now less relaxed; you need to make sure the full chord is pressed within the `COMBO_TERM`. +Misfires are less common but if you type multiple combos fast, there is a +chance that the latter ones might not activate properly. + +#### `#define COMBO_NO_TIMER` + +By defining `COMBO_NO_TIMER`, the timer is disabled completely and combos are activated on the first key release. +This also disables the "must hold" functionalities as they just wouldn't work at all. + +### Customizable key releases + +By defining `COMBO_PROCESS_KEY_RELEASE` and implementing the function `bool process_combo_key_release(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode)`, you can run your custom code on each key release after a combo was activated. For example you could change the RGB colors, activate haptics, or alter the modifiers. + +You can also release a combo early by returning `true` from the function. + +Here's an example where a combo resolves to two modifiers, and on key releases the modifiers are unregistered one by one, depending on which key was released. + +```c +enum combos { + AB_MODS +}; + +const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END}; + +combo_t key_combos[] = { + [AB_MODS] = COMBO(ab_combo, LCTL(KC_LSFT)), +}; + +bool process_combo_key_release(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode) { + switch (combo_index) { + case AB_MODS: + switch(keycode) { + case KC_A: + unregister_mods(MOD_MASK_CTRL); + break; + case KC_B: + unregister_mods(MOD_MASK_SHIFT); + break; + } + return false; // do not release combo + } + return false; +} +``` +### Layer independent combos + +If you, for example, use multiple base layers for different key layouts, one for QWERTY, and another one for Colemak, you might want your combos to work from the same key positions on all layers. Defining the same combos again for another layout is redundant and takes more memory. The solution is to just check the keycodes from one layer. + +With `#define COMBO_ONLY_FROM_LAYER 0` in config.h, the combos' keys are always checked from layer `0`, even if other layers are active. + +#### Combo reference layers by layer. + +If not using `COMBO_ONLY_FROM_LAYER` it is possible to specify a +combo reference layer for any layer using the `combo_ref_from_layer` hook. +The combo macros automatically create this function from the `COMBO_REF_LAYER()` +entries given. + +This function returns the assigned reference layer for the current layer. +if there is no match, it returns the default reference layer if set, +or the current layer otherwise. A default layer can be set with +`DEFAULT_REF_LAYER(_MY_COMBO_REF_LAYER)` + +If not set, the default reference layer selection from the automatically generated +`combo-ref-from-layer()` will be the current layer. + +The following `combo_ref_from_layer` function +will give a reference layer of _QWERTY for the _DVORAK layer and +will give the _NAV layer as a reference to it's self. All other layers +will have the default for their combo reference layer. If the default +is not set, all other layers will reference themselves. + +```c +#define COMBO_REF_DEFAULT _MY_COMBO_LAYER + +uint8_t combo_ref_from_layer(uint8_t layer){ + switch (get_highest_layer(layer_state)){ + case _DVORAK: return _QWERTY; + case _NAV: return _NAV; + default: return _MY_COMBO_LAYER; + } + return layer; // important if default is not in case. +} +``` + +The equivalent definition using the combo macros is this: + +```c +COMBO_REF_LAYER(_DVORAK, _QWERTY) +COMBO_REF_LAYER(_NAV, _NAV) +DEFAULT_REF_LAYER(_MY_COMBO_LAYER). +``` + + +## User callbacks + +In addition to the keycodes, there are a few functions that you can use to set the status, or check it: + +|Function |Description | +|-----------|--------------------------------------------------------------------| +| `combo_enable()` | Enables the combo feature | +| `combo_disable()` | Disables the combo feature, and clears the combo buffer | +| `combo_toggle()` | Toggles the state of the combo feature | +| `is_combo_enabled()` | Returns the status of the combo feature state (true or false) | + + +## Dictionary Management + +Having 3 places to update when adding new combos or altering old ones does become cumbersome when you have a lot of combos. We can alleviate this with some magic! ... If you consider C macros magic. +First, you need to add `VPATH += keyboards/gboards` to your `rules.mk`. Next, include the file `g/keymap_combo.h` in your `keymap.c`. + +::: warning +This functionality uses the same `process_combo_event` function as `COMBO_ACTION` macros do, so you cannot use the function yourself in your keymap. Instead, you have to define the `case`s of the `switch` statement by themselves within `inject.h`, which `g/keymap_combo.h` will then include into the function. +::: + +Then, write your combos in `combos.def` file in the following manner: + +```c +// Alternate reference layers by layer +// Layer Reference layer +COMBO_REF_LAYER(_DVORAK, _QWERTY) // reference the qwerty layer for dvorak. +COMBO_REF_LAYER(_NAV, _NAV) // explicit reference to self instead of the default. + +// name result chord keys +COMB(AB_ESC, KC_ESC, KC_A, KC_B) +COMB(JK_TAB, KC_TAB, KC_J, KC_K) +COMB(JKL_SPC, KC_SPC, KC_J, KC_K, KC_L) +COMB(BSSL_CLR, KC_NO, KC_BSPC, KC_LSFT) // using KC_NO as the resulting keycode is the same as COMBO_ACTION before. +COMB(QW_UNDO, C(KC_Z), KC_Q, KC_W) +SUBS(TH_THE, "the", KC_T, KC_H) // SUBS uses SEND_STRING to output the given string. +... +``` + +For small to huge ready made dictionaries of combos, you can check out http://combos.gboards.ca/. diff --git a/docs/features/command.md b/docs/features/command.md new file mode 100644 index 0000000000..7ad45103c7 --- /dev/null +++ b/docs/features/command.md @@ -0,0 +1,51 @@ +# Command + +Command, formerly known as Magic, is a way to change your keyboard's behavior without having to flash or unplug it to use [Bootmagic Lite](bootmagic). There is a lot of overlap between this functionality and the [Magic Keycodes](../keycodes_magic). Wherever possible we encourage you to use that feature instead of Command. + +On some keyboards Command is disabled by default. If this is the case, it must be explicitly enabled in your `rules.mk`: + +```make +COMMAND_ENABLE = yes +``` + +## Usage + +To use Command, hold down the key combination defined by the `IS_COMMAND()` macro. By default this is Left Shift+Right Shift. Then, press the key corresponding to the command you want. For example, to output the current QMK version to the QMK Toolbox console, press Left Shift+Right Shift+`V`. + +## Configuration + +If you would like to change the key assignments for Command, `#define` these in your `config.h` at either the keyboard or keymap level. All keycode assignments here must omit the `KC_` prefix. + +|Define |Default |Description | +|------------------------------------|--------------------------------|------------------------------------------------| +|`IS_COMMAND()` |`(get_mods() == MOD_MASK_SHIFT)`|The key combination to activate Command | +|`MAGIC_KEY_SWITCH_LAYER_WITH_FKEYS` |`true` |Set default layer with the Function row | +|`MAGIC_KEY_SWITCH_LAYER_WITH_NKEYS` |`true` |Set default layer with the number keys | +|`MAGIC_KEY_SWITCH_LAYER_WITH_CUSTOM`|`false` |Set default layer with `MAGIC_KEY_LAYER0..9` | +|`MAGIC_KEY_DEBUG` |`D` |Toggle debugging over serial | +|`MAGIC_KEY_DEBUG_MATRIX` |`X` |Toggle key matrix debugging | +|`MAGIC_KEY_DEBUG_KBD` |`K` |Toggle keyboard debugging | +|`MAGIC_KEY_DEBUG_MOUSE` |`M` |Toggle mouse debugging | +|`MAGIC_KEY_CONSOLE` |`C` |Enable the Command console | +|`MAGIC_KEY_VERSION` |`V` |Print the running QMK version to the console | +|`MAGIC_KEY_STATUS` |`S` |Print the current keyboard status to the console| +|`MAGIC_KEY_HELP` |`H` |Print Command help to the console | +|`MAGIC_KEY_HELP_ALT` |`SLASH` |Print Command help to the console (alternate) | +|`MAGIC_KEY_LAYER0` |`0` |Make layer 0 the default layer | +|`MAGIC_KEY_LAYER0_ALT` |`GRAVE` |Make layer 0 the default layer (alternate) | +|`MAGIC_KEY_LAYER1` |`1` |Make layer 1 the default layer | +|`MAGIC_KEY_LAYER2` |`2` |Make layer 2 the default layer | +|`MAGIC_KEY_LAYER3` |`3` |Make layer 3 the default layer | +|`MAGIC_KEY_LAYER4` |`4` |Make layer 4 the default layer | +|`MAGIC_KEY_LAYER5` |`5` |Make layer 5 the default layer | +|`MAGIC_KEY_LAYER6` |`6` |Make layer 6 the default layer | +|`MAGIC_KEY_LAYER7` |`7` |Make layer 7 the default layer | +|`MAGIC_KEY_LAYER8` |`8` |Make layer 8 the default layer | +|`MAGIC_KEY_LAYER9` |`9` |Make layer 9 the default layer | +|`MAGIC_KEY_BOOTLOADER` |`B` |Jump to bootloader | +|`MAGIC_KEY_BOOTLOADER_ALT` |`ESC` |Jump to bootloader (alternate) | +|`MAGIC_KEY_LOCK` |`CAPS` |Lock the keyboard so nothing can be typed | +|`MAGIC_KEY_EEPROM` |`E` |Print stored EEPROM config to the console | +|`MAGIC_KEY_EEPROM_CLEAR` |`BSPACE` |Clear the EEPROM | +|`MAGIC_KEY_NKRO` |`N` |Toggle N-Key Rollover (NKRO) | +|`MAGIC_KEY_SLEEP_LED` |`Z` |Toggle LED when computer is sleeping | diff --git a/docs/features/digitizer.md b/docs/features/digitizer.md new file mode 100644 index 0000000000..e524877636 --- /dev/null +++ b/docs/features/digitizer.md @@ -0,0 +1,119 @@ +# Digitizer {#digitizer} + +Digitizers allow the mouse cursor to be placed at absolute coordinates, unlike the [Pointing Device](pointing_device) feature which applies relative displacements. + +This feature implements a stylus device with a tip switch and barrel switch (generally equivalent to the primary and secondary mouse buttons respectively). Tip pressure is not currently implemented. + +## Usage {#usage} + +Add the following to your `rules.mk`: + +```make +DIGITIZER_ENABLE = yes +``` + +## Positioning {#positioning} + +The X and Y coordinates are normalized, meaning their value must be set between 0 and 1. For the X component, the value `0` is the leftmost position, whereas the value `1` is the rightmost position. Similarly for the Y component, `0` is at the top and `1` at the bottom. + +::: tip +Since there is no display attached, the OS will likely map these coordinates to the virtual desktop. This may be important to know if you have multiple monitors. +::: + +## Examples {#examples} + +This example simply places the cursor in the middle of the screen: + +```c +digitizer_in_range_on(); +digitizer_set_position(0.5, 0.5); +``` + +The "in range" indicator is required to be on for the change in coordinates to be taken. It can then be turned off again to signal the end of the digitizer interaction, but it is not strictly required. + +You can also modify the digitizer state directly, if you need to change multiple fields in a single report: + +```c +digitizer_state.in_range = true; +digitizer_state.dirty = true; +digitizer_flush(); +``` + +`digitizer_state` is a struct of type `digitizer_t`. + + +## API {#api} + +### `struct digitizer_t` {#api-digitizer-t} + +Contains the state of the digitizer. + +#### Members {#api-digitizer-t-members} + + - `bool in_range` + Indicates to the host that the contact is within range (ie. close to or in contact with the digitizer surface). + - `bool tip` + The state of the tip switch. + - `bool barrel` + The state of the barrel switch. + - `float x` + The X coordinate of the digitizer contact. + - `float y` + The Y coordinate of the digitizer contact. + - `bool dirty` + Whether the current state needs to be sent to the host. + +--- + +### `void digitizer_flush(void)` {#api-digitizer-flush} + +Send the digitizer report to the host if it is marked as dirty. + +--- + +### `void digitizer_in_range_on(void)` {#api-digitizer-in-range-on} + +Assert the "in range" indicator, and flush the report. + +--- + +### `void digitizer_in_range_off(void)` {#api-digitizer-in-range-off} + +Deassert the "in range" indicator, and flush the report. + +--- + +### `void digitizer_tip_switch_on(void)` {#api-digitizer-tip-switch-on} + +Assert the tip switch, and flush the report. + +--- + +### `void digitizer_tip_switch_off(void)` {#api-digitizer-tip-switch-off} + +Deassert the tip switch, and flush the report. + +--- + +### `void digitizer_barrel_switch_on(void)` {#api-digitizer-barrel-switch-on} + +Assert the barrel switch, and flush the report. + +--- + +### `void digitizer_barrel_switch_off(void)` {#api-digitizer-barrel-switch-off} + +Deassert the barrel switch, and flush the report. + +--- + +### `void digitizer_set_position(float x, float y)` {#api-digitizer-set-position} + +Set the absolute X and Y position of the digitizer contact, and flush the report. + +#### Arguments {#api-digitizer-set-position-arguments} + + - `float x` + The X value of the contact position, from 0 to 1. + - `float y` + The Y value of the contact position, from 0 to 1. diff --git a/docs/features/dip_switch.md b/docs/features/dip_switch.md new file mode 100644 index 0000000000..738331bef0 --- /dev/null +++ b/docs/features/dip_switch.md @@ -0,0 +1,132 @@ +# DIP Switches + +DIP switches are supported by adding this to your `rules.mk`: + + DIP_SWITCH_ENABLE = yes + +and this to your `config.h`: + +```c +// Connects each switch in the dip switch to the GPIO pin of the MCU +#define DIP_SWITCH_PINS { B14, A15, A10, B9 } +// For split keyboards, you can separately define the right side pins +#define DIP_SWITCH_PINS_RIGHT { ... } +``` + +or + +```c +// Connect each switch in the DIP switch to an unused intersections in the key matrix. +#define DIP_SWITCH_MATRIX_GRID { {0,6}, {1,6}, {2,6} } // List of row and col pairs +``` + +## DIP Switch map {#dip-switch-map} + +DIP Switch mapping may be added to your `keymap.c`, which replicates the normal keyswitch functionality, but with dip switches. Add this to your keymap's `rules.mk`: + +```make +DIP_SWITCH_MAP_ENABLE = yes +``` + +Your `keymap.c` will then need a dip switch mapping defined (for two dip switches): + +```c +#if defined(DIP_SWITCH_MAP_ENABLE) +const uint16_t PROGMEM dip_switch_map[NUM_DIP_SWITCHES][NUM_DIP_STATES] = { + DIP_SWITCH_OFF_ON(DF(0), DF(1)), + DIP_SWITCH_OFF_ON(EC_NORM, EC_SWAP) +}; +#endif +``` + +::: tip +This should only be enabled at the keymap level. +::: + +## Callbacks + +The callback functions can be inserted into your `.c`: + +```c +bool dip_switch_update_kb(uint8_t index, bool active) { + if (!dip_switch_update_user(index, active)) { return false; } + return true; +} +``` + + +or `keymap.c`: + +```c +bool dip_switch_update_user(uint8_t index, bool active) { + switch (index) { + case 0: + if(active) { audio_on(); } else { audio_off(); } + break; + case 1: + if(active) { clicky_on(); } else { clicky_off(); } + break; + case 2: + if(active) { music_on(); } else { music_off(); } + break; + case 3: + if (active) { + #ifdef AUDIO_ENABLE + PLAY_SONG(plover_song); + #endif + layer_on(_PLOVER); + } else { + #ifdef AUDIO_ENABLE + PLAY_SONG(plover_gb_song); + #endif + layer_off(_PLOVER); + } + break; + } + return true; +} +``` + +Additionally, we support bit mask functions which allow for more complex handling. + + +```c +bool dip_switch_update_mask_kb(uint32_t state) { + if (!dip_switch_update_mask_user(state)) { return false; } + return true; +} +``` + + +or `keymap.c`: + +```c +bool dip_switch_update_mask_user(uint32_t state) { + if (state & (1UL<<0) && state & (1UL<<1)) { + layer_on(_ADJUST); // C on esc + } else { + layer_off(_ADJUST); + } + if (state & (1UL<<0)) { + layer_on(_TEST_A); // A on ESC + } else { + layer_off(_TEST_A); + } + if (state & (1UL<<1)) { + layer_on(_TEST_B); // B on esc + } else { + layer_off(_TEST_B); + } + return true; +} +``` + +## Hardware + +### Connects each switch in the dip switch to the GPIO pin of the MCU + +One side of the DIP switch should be wired directly to the pin on the MCU, and the other side to ground. It should not matter which side is connected to which, as it should be functionally the same. + +### Connect each switch in the DIP switch to an unused intersections in the key matrix. + +As with the keyswitch, a diode and DIP switch connect the ROW line to the COL line. diff --git a/docs/features/dynamic_macros.md b/docs/features/dynamic_macros.md new file mode 100644 index 0000000000..a642ced43e --- /dev/null +++ b/docs/features/dynamic_macros.md @@ -0,0 +1,69 @@ +# Dynamic Macros: Record and Replay Macros in Runtime + +QMK supports temporary macros created on the fly. We call these Dynamic Macros. They are defined by the user from the keyboard and are lost when the keyboard is unplugged or otherwise rebooted. + +You can store one or two macros and they may have a combined total of 128 keypresses. You can increase this size at the cost of RAM. + +To enable them, first include `DYNAMIC_MACRO_ENABLE = yes` in your `rules.mk`. Then, add the following keys to your keymap: + +|Key |Alias |Description | +|---------------------------------|---------|--------------------------------------------------| +|`QK_DYNAMIC_MACRO_RECORD_START_1`|`DM_REC1`|Start recording Macro 1 | +|`QK_DYNAMIC_MACRO_RECORD_START_2`|`DM_REC2`|Start recording Macro 2 | +|`QK_DYNAMIC_MACRO_PLAY_1` |`DM_PLY1`|Replay Macro 1 | +|`QK_DYNAMIC_MACRO_PLAY_2` |`DM_PLY2`|Replay Macro 2 | +|`QK_DYNAMIC_MACRO_RECORD_STOP` |`DM_RSTP`|Finish the macro that is currently being recorded.| + +That should be everything necessary. + +To start recording the macro, press either `DM_REC1` or `DM_REC2`. + +To finish the recording, press the `DM_RSTP` layer button. You can also press `DM_REC1` or `DM_REC2` again to stop the recording. + +To replay the macro, press either `DM_PLY1` or `DM_PLY2`. + +It is possible to replay a macro as part of a macro. It's ok to replay macro 2 while recording macro 1 and vice versa but never create recursive macros i.e. macro 1 that replays macro 1. If you do so and the keyboard will get unresponsive, unplug the keyboard and plug it again. You can disable this completely by defining `DYNAMIC_MACRO_NO_NESTING` in your `config.h` file. + +::: tip +For the details about the internals of the dynamic macros, please read the comments in the `process_dynamic_macro.h` and `process_dynamic_macro.c` files. +::: + +## Customization + +There are a number of options added that should allow some additional degree of customization + +|Define |Default |Description | +|----------------------------|----------------|-----------------------------------------------------------------------------------------------------------------| +|`DYNAMIC_MACRO_SIZE` |128 |Sets the amount of memory that Dynamic Macros can use. This is a limited resource, dependent on the controller. | +|`DYNAMIC_MACRO_USER_CALL` |*Not defined* |Defining this falls back to using the user `keymap.c` file to trigger the macro behavior. | +|`DYNAMIC_MACRO_NO_NESTING` |*Not Defined* |Defining this disables the ability to call a macro from another macro (nested macros). | +|`DYNAMIC_MACRO_DELAY` |*Not Defined* |Sets the waiting time (ms unit) when sending each key. | + + +If the LEDs start blinking during the recording with each keypress, it means there is no more space for the macro in the macro buffer. To fit the macro in, either make the other macro shorter (they share the same buffer) or increase the buffer size by adding the `DYNAMIC_MACRO_SIZE` define in your `config.h` (default value: 128; please read the comments for it in the header). + + +### DYNAMIC_MACRO_USER_CALL + +For users of the earlier versions of dynamic macros: It is still possible to finish the macro recording using just the layer modifier used to access the dynamic macro keys, without a dedicated `DM_RSTP` key. If you want this behavior back, add `#define DYNAMIC_MACRO_USER_CALL` to your `config.h` and insert the following snippet at the beginning of your `process_record_user()` function: + +```c + uint16_t macro_kc = (keycode == MO(_DYN) ? DM_RSTP : keycode); + + if (!process_record_dynamic_macro(macro_kc, record)) { + return false; + } +``` + +### User Hooks + +There are a number of hooks that you can use to add custom functionality and feedback options to Dynamic Macro feature. This allows for some additional degree of customization. + +Note, that direction indicates which macro it is, with `1` being Macro 1, `-1` being Macro 2, and 0 being no macro. + +* `dynamic_macro_record_start_user(int8_t direction)` - Triggered when you start recording a macro. +* `dynamic_macro_play_user(int8_t direction)` - Triggered when you play back a macro. +* `dynamic_macro_record_key_user(int8_t direction, keyrecord_t *record)` - Triggered on each keypress while recording a macro. +* `dynamic_macro_record_end_user(int8_t direction)` - Triggered when the macro recording is stopped. + +Additionally, you can call `dynamic_macro_led_blink()` to flash the backlights if that feature is enabled. diff --git a/docs/features/encoders.md b/docs/features/encoders.md new file mode 100644 index 0000000000..3d1cac79af --- /dev/null +++ b/docs/features/encoders.md @@ -0,0 +1,186 @@ +# Encoders + +Basic (EC11 compatible) encoders are supported by adding this to your `rules.mk`: + +```make +ENCODER_ENABLE = yes +``` + +and this to your `config.h`: + +```c +#define ENCODERS_PAD_A { B12 } +#define ENCODERS_PAD_B { B13 } +``` + +Each PAD_A/B variable defines an array so multiple encoders can be defined, e.g.: + +```c +#define ENCODERS_PAD_A { encoder1a, encoder2a } +#define ENCODERS_PAD_B { encoder1b, encoder2b } +``` + +If your encoder's clockwise directions are incorrect, you can swap the A & B pad definitions. They can also be flipped with a define: + +```c +#define ENCODER_DIRECTION_FLIP +``` + +Additionally, the resolution, which defines how many pulses the encoder registers between each detent, can be defined with: + +```c +#define ENCODER_RESOLUTION 4 +``` + +It can also be defined per-encoder, by instead defining: + +```c +#define ENCODER_RESOLUTIONS { 4, 2 } +``` + +For 4× encoders you also can assign default position if encoder skips pulses when it changes direction. For example, if your encoder send high level on both pins by default, define this: + +```c +#define ENCODER_DEFAULT_POS 0x3 +``` + +## Split Keyboards + +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 } +``` + +If the `_RIGHT` definitions aren't specified in your `config.h`, then the non-`_RIGHT` versions will be applied to both sides of the split. + +Additionally, if one side does not have an encoder, you can specify `{}` for the pins/resolution -- for example, a split keyboard with only a right-side encoder: + +```c +#define ENCODERS_PAD_A { } +#define ENCODERS_PAD_B { } +#define ENCODER_RESOLUTIONS { } +#define ENCODERS_PAD_A_RIGHT { B12 } +#define ENCODERS_PAD_B_RIGHT { B13 } +#define ENCODER_RESOLUTIONS_RIGHT { 4 } +``` + +::: warning +Keep in mind that whenver you change the encoder resolution, you will need to reflash the half that has the encoder affected by the change. +::: + +## Encoder map {#encoder-map} + +Encoder mapping may be added to your `keymap.c`, which replicates the normal keyswitch layer handling functionality, but with encoders. Add this to your keymap's `rules.mk`: + +```make +ENCODER_MAP_ENABLE = yes +``` + +Your `keymap.c` will then need an encoder mapping defined (for four layers and two encoders): + +```c +#if defined(ENCODER_MAP_ENABLE) +const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][NUM_DIRECTIONS] = { + [0] = { ENCODER_CCW_CW(KC_MS_WH_UP, KC_MS_WH_DOWN), ENCODER_CCW_CW(KC_VOLD, KC_VOLU) }, + [1] = { ENCODER_CCW_CW(RGB_HUD, RGB_HUI), ENCODER_CCW_CW(RGB_SAD, RGB_SAI) }, + [2] = { ENCODER_CCW_CW(RGB_VAD, RGB_VAI), ENCODER_CCW_CW(RGB_SPD, RGB_SPI) }, + [3] = { ENCODER_CCW_CW(RGB_RMOD, RGB_MOD), ENCODER_CCW_CW(KC_RIGHT, KC_LEFT) }, +}; +#endif +``` + +::: tip +This should only be enabled at the keymap level. +::: + +Using encoder mapping pumps events through the normal QMK keycode processing pipeline, resulting in a _keydown/keyup_ combination pushed through `process_record_xxxxx()`. To configure the amount of time between the encoder "keyup" and "keydown", you can add the following to your `config.h`: + +```c +#define ENCODER_MAP_KEY_DELAY 10 +``` + +::: tip +By default, the encoder map delay matches the value of `TAP_CODE_DELAY`. +::: + +## Callbacks + +::: tip +[**Default Behaviour**](https://github.com/qmk/qmk_firmware/blob/master/quantum/encoder.c#L79-): all encoders installed will function as volume up (`KC_VOLU`) on clockwise rotation and volume down (`KC_VOLD`) on counter-clockwise rotation. If you do not wish to override this, no further configuration is necessary. +::: + +If you would like the alter the default behaviour, and are not using `ENCODER_MAP_ENABLE = yes`, the callback functions can be inserted into your `.c`: + +```c +bool encoder_update_kb(uint8_t index, bool clockwise) { + if (!encoder_update_user(index, clockwise)) { + return false; /* Don't process further events if user function exists and returns false */ + } + if (index == 0) { /* First encoder */ + if (clockwise) { + tap_code(KC_PGDN); + } else { + tap_code(KC_PGUP); + } + } else if (index == 1) { /* Second encoder */ + if (clockwise) { + rgb_matrix_increase_hue(); + } else { + rgb_matrix_decrease_hue(); + } + } + return true; +} +``` + +or `keymap.c`: + +```c +bool encoder_update_user(uint8_t index, bool clockwise) { + if (index == 0) { /* First encoder */ + if (clockwise) { + tap_code(KC_PGDN); + } else { + tap_code(KC_PGUP); + } + } else if (index == 1) { /* Second encoder */ + if (clockwise) { + rgb_matrix_increase_hue(); + } else { + rgb_matrix_decrease_hue(); + } + } + return false; +} +``` + +::: warning +If you return `true` in the keymap level `_user` function, it will allow the keyboard/core level encoder code to run on top of your own. Returning `false` will override the keyboard level function, if setup correctly. This is generally the safest option to avoid confusion. +::: + +## Hardware + +The A an B lines of the encoders should be wired directly to the MCU, and the C/common lines should be wired to ground. + +## Multiple Encoders + +Multiple encoders may share pins so long as each encoder has a distinct pair of pins when the following conditions are met: +- using detent encoders +- pads must be high at the detent stability point which is called 'default position' in QMK +- no more than two encoders sharing a pin can be turned at the same time + +For example you can support two encoders using only 3 pins like this +``` +#define ENCODERS_PAD_A { B1, B1 } +#define ENCODERS_PAD_B { B2, B3 } +``` + +You could even support three encoders using only three pins (one per encoder) however in this configuration, rotating two encoders which share pins simultaneously will often generate incorrect output. For example: +``` +#define ENCODERS_PAD_A { B1, B1, B2 } +#define ENCODERS_PAD_B { B2, B3, B3 } +``` +Here rotating Encoder 0 `B1 B2` and Encoder 1 `B1 B3` could be interpreted as rotating Encoder 2 `B2 B3` or `B3 B2` depending on the timing. This may still be a useful configuration depending on your use case diff --git a/docs/features/grave_esc.md b/docs/features/grave_esc.md new file mode 100644 index 0000000000..09d098ee4e --- /dev/null +++ b/docs/features/grave_esc.md @@ -0,0 +1,32 @@ +# Grave Escape + +If you're using a 60% keyboard, or any other layout with no F-row, you will have noticed that there is no dedicated Escape key. Grave Escape is a feature that allows you to share the grave key (` and `~`) with Escape. + +## Usage + +Replace the `KC_GRV` key in your keymap (usually to the left of the `1` key) with `QK_GESC`. Most of the time this key will output `KC_ESC` when pressed. However, when Shift or GUI are held down it will output `KC_GRV` instead. + +## What Your OS Sees + +If Mary presses `QK_GESC` on her keyboard, the OS will see an KC_ESC character. Now if Mary holds Shift down and presses `QK_GESC` it will output `~`, or a shifted backtick. Now if she holds GUI/CMD/WIN, it will output a simple ` character. + +## Keycodes + +|Key |Aliases |Description | +|-----------------|---------|------------------------------------------------------------------| +|`QK_GRAVE_ESCAPE`|`QK_GESC`|Escape when pressed, ` when Shift or GUI are held| + +### Caveats + +On macOS, Command+` is by default mapped to "Move focus to next window" so it will not output a backtick. Additionally, Terminal always recognises this shortcut to cycle between windows, even if the shortcut is changed in the Keyboard preferences. + +## Configuration + +There are several possible key combinations this will break, among them Control+Shift+Escape on Windows and Command+Option+Escape on macOS. To work around this, you can `#define` these options in your `config.h`: + +|Define |Description | +|--------------------------|-----------------------------------------| +|`GRAVE_ESC_ALT_OVERRIDE` |Always send Escape if Alt is pressed | +|`GRAVE_ESC_CTRL_OVERRIDE` |Always send Escape if Control is pressed | +|`GRAVE_ESC_GUI_OVERRIDE` |Always send Escape if GUI is pressed | +|`GRAVE_ESC_SHIFT_OVERRIDE`|Always send Escape if Shift is pressed | diff --git a/docs/features/haptic_feedback.md b/docs/features/haptic_feedback.md new file mode 100644 index 0000000000..52667965a0 --- /dev/null +++ b/docs/features/haptic_feedback.md @@ -0,0 +1,214 @@ +# Haptic Feedback + +## Haptic feedback rules.mk options + +The following options are currently available for haptic feedback in `rules.mk`: + +```make +HAPTIC_ENABLE = yes + +HAPTIC_DRIVER = drv2605l +# or +HAPTIC_DRIVER = solenoid +``` + +The following `config.h` settings are available for all types of haptic feedback: + +| Settings | Default | Description | +|--------------------------------------|---------------|---------------------------------------------------------------------------------------------------------------| +|`HAPTIC_ENABLE_PIN` | *Not defined* |Configures a pin to enable a boost converter for some haptic solution, often used with solenoid drivers. | +|`HAPTIC_ENABLE_PIN_ACTIVE_LOW` | *Not defined* |If defined then the haptic enable pin is active-low. | +|`HAPTIC_ENABLE_STATUS_LED` | *Not defined* |Configures a pin to reflect the current enabled/disabled status of haptic feedback. | +|`HAPTIC_ENABLE_STATUS_LED_ACTIVE_LOW` | *Not defined* |If defined then the haptic status led will be active-low. | +|`HAPTIC_OFF_IN_LOW_POWER` | `0` |If set to `1`, haptic feedback is disabled before the device is configured, and while the device is suspended. | + +## Known Supported Hardware + +| Name | Description | +|--------------------|-------------------------------------------------| +| [LV061228B-L65-A](https://www.digikey.com/product-detail/en/jinlong-machinery-electronics-inc/LV061228B-L65-A/1670-1050-ND/7732325) | z-axis 2v LRA | +| [Mini Motor Disc](https://www.adafruit.com/product/1201) | small 2-5v ERM | + +## Haptic Keycodes + +Not all keycodes below will work depending on which haptic mechanism you have chosen. + +| Key | Aliases | Description | +|-----------------------------|---------|-------------------------------------------------------| +|`QK_HAPTIC_ON` |`HF_ON` | Turn haptic feedback on | +|`QK_HAPTIC_OFF` |`HF_OFF` | Turn haptic feedback off | +|`QK_HAPTIC_TOGGLE` |`HF_TOGG`| Toggle haptic feedback on/off | +|`QK_HAPTIC_RESET` |`HF_RST` | Reset haptic feedback config to default | +|`QK_HAPTIC_FEEDBACK_TOGGLE` |`HF_FDBK`| Toggle feedback to occur on keypress, release or both | +|`QK_HAPTIC_BUZZ_TOGGLE` |`HF_BUZZ`| Toggle solenoid buzz on/off | +|`QK_HAPTIC_MODE_NEXT` |`HF_NEXT`| Go to next DRV2605L waveform | +|`QK_HAPTIC_MODE_PREVIOUS` |`HF_PREV`| Go to previous DRV2605L waveform | +|`QK_HAPTIC_CONTINUOUS_TOGGLE`|`HF_CONT`| Toggle continuous haptic mode on/off | +|`QK_HAPTIC_CONTINUOUS_UP` |`HF_CONU`| Increase DRV2605L continous haptic strength | +|`QK_HAPTIC_CONTINUOUS_DOWN` |`HF_COND`| Decrease DRV2605L continous haptic strength | +|`QK_HAPTIC_DWELL_UP` |`HF_DWLU`| Increase Solenoid dwell time | +|`QK_HAPTIC_DWELL_DOWN` |`HF_DWLD`| Decrease Solenoid dwell time | + +### Solenoids + +The solenoid code supports relay switches, and similar hardware, as well as solenoids. + +For a regular solenoid, you will need a build a circuit to drive the solenoid through a mosfet as most MCU will not be able to provide the current needed to drive the coil in the solenoid. + +[Wiring diagram provided by Adafruit](https://cdn-shop.adafruit.com/product-files/412/solenoid_driver.pdf) + +For relay switches, the hardware may already contain all of that ciruitry, and just require VCC, GND and a data pin. + +| Settings | Default | Description | +|----------------------------|----------------------|--------------------------------------------------------------| +|`SOLENOID_PIN` | *Not defined* |Configures the pin that the switch is connected to. | +|`SOLENOID_PIN_ACTIVE_LOW` | *Not defined* |If defined then the switch trigger pin is active low. | +|`SOLENOID_PINS` | *Not defined* |Configures an array of pins to be used for switch activation. | +|`SOLENOID_PINS_ACTIVE_LOW` | *Not defined* |Allows you to specify how each pin is pulled for activation. | +|`SOLENOID_RANDOM_FIRE` | *Not defined* |When there are multiple solenoids, will select a random one to fire.| +|`SOLENOID_DEFAULT_DWELL` | `12` ms |Configures the default dwell time for the switch. | +|`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 `HF_DWL*` keycodes are sent. | +|`SOLENOID_DEFAULT_BUZZ` | `0` (disabled) |On `HF_RST` buzz is set "on" if this is "1" | +|`SOLENOID_BUZZ_ACTUATED` | `SOLENOID_MIN_DWELL` |Actuated-time when the switch is in buzz mode. | +|`SOLENOID_BUZZ_NONACTUATED` | `SOLENOID_MIN_DWELL` |Non-Actuated-time when the switch 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. + +### DRV2605L + +DRV2605L is controlled over i2c protocol, and has to be connected to the SDA and SCL pins, these varies depending on the MCU in use. + +#### Feedback motor setup + +This driver supports 2 different feedback motors. Set the following in your `config.h` based on which motor you have selected. + +##### ERM + +Eccentric Rotating Mass vibration motors (ERM) is motor with a off-set weight attached so when drive signal is attached, the off-set weight spins and causes a sinusoidal wave that translate into vibrations. + +```c +#define DRV2605L_FB_ERM_LRA 0 +#define DRV2605L_FB_BRAKEFACTOR 3 /* For 1x:0, 2x:1, 3x:2, 4x:3, 6x:4, 8x:5, 16x:6, Disable Braking:7 */ +#define DRV2605L_FB_LOOPGAIN 1 /* For Low:0, Medium:1, High:2, Very High:3 */ + +/* Please refer to your datasheet for the optimal setting for your specific motor. */ +#define DRV2605L_RATED_VOLTAGE 3 +#define DRV2605L_V_PEAK 5 +``` +##### LRA + +Linear resonant actuators (LRA, also know as a linear vibrator) works different from a ERM. A LRA has a weight and magnet suspended by springs and a voice coil. When the drive signal is applied, the weight would be vibrate on a single axis (side to side or up and down). Since the weight is attached to a spring, there is a resonance effect at a specific frequency. This frequency is where the LRA will operate the most efficiently. Refer to the motor's datasheet for the recommanded range for this frequency. + +```c +#define DRV2605L_FB_ERM_LRA 1 +#define DRV2605L_FB_BRAKEFACTOR 3 /* For 1x:0, 2x:1, 3x:2, 4x:3, 6x:4, 8x:5, 16x:6, Disable Braking:7 */ +#define DRV2605L_FB_LOOPGAIN 1 /* For Low:0, Medium:1, High:2, Very High:3 */ + +/* Please refer to your datasheet for the optimal setting for your specific motor. */ +#define DRV2605L_RATED_VOLTAGE 2 +#define DRV2605L_V_PEAK 2.8 +#define DRV2605L_V_RMS 2.0 +#define DRV2605L_V_PEAK 2.1 +#define DRV2605L_F_LRA 205 /* resonance freq */ +``` + +#### DRV2605L waveform library + +DRV2605L comes with preloaded library of various waveform sequences that can be called and played. If writing a macro, these waveforms can be played using `DRV_pulse(*sequence name or number*)` + +List of waveform sequences from the datasheet: + +|seq# | Sequence name |seq# | Sequence name |seq# |Sequence name | +|-----|---------------------|-----|-----------------------------------|-----|--------------------------------------| +| 1 | strong_click | 43 | lg_dblclick_med_60 | 85 | transition_rampup_med_smooth2 | +| 2 | strong_click_60 | 44 | lg_dblsharp_tick | 86 | transition_rampup_short_smooth1 | +| 3 | strong_click_30 | 45 | lg_dblsharp_tick_80 | 87 | transition_rampup_short_smooth2 | +| 4 | sharp_click | 46 | lg_dblsharp_tick_60 | 88 | transition_rampup_long_sharp1 | +| 5 | sharp_click_60 | 47 | buzz | 89 | transition_rampup_long_sharp2 | +| 6 | sharp_click_30 | 48 | buzz_80 | 90 | transition_rampup_med_sharp1 | +| 7 | soft_bump | 49 | buzz_60 | 91 | transition_rampup_med_sharp2 | +| 8 | soft_bump_60 | 50 | buzz_40 | 92 | transition_rampup_short_sharp1 | +| 9 | soft_bump_30 | 51 | buzz_20 | 93 | transition_rampup_short_sharp2 | +| 10 | dbl_click | 52 | pulsing_strong | 94 | transition_rampdown_long_smooth1_50 | +| 11 | dbl_click_60 | 53 | pulsing_strong_80 | 95 | transition_rampdown_long_smooth2_50 | +| 12 | trp_click | 54 | pulsing_medium | 96 | transition_rampdown_med_smooth1_50 | +| 13 | soft_fuzz | 55 | pulsing_medium_80 | 97 | transition_rampdown_med_smooth2_50 | +| 14 | strong_buzz | 56 | pulsing_sharp | 98 | transition_rampdown_short_smooth1_50 | +| 15 | alert_750ms | 57 | pulsing_sharp_80 | 99 | transition_rampdown_short_smooth2_50 | +| 16 | alert_1000ms | 58 | transition_click | 100 | transition_rampdown_long_sharp1_50 | +| 17 | strong_click1 | 59 | transition_click_80 | 101 | transition_rampdown_long_sharp2_50 | +| 18 | strong_click2_80 | 60 | transition_click_60 | 102 | transition_rampdown_med_sharp1_50 | +| 19 | strong_click3_60 | 61 | transition_click_40 | 103 | transition_rampdown_med_sharp2_50 | +| 20 | strong_click4_30 | 62 | transition_click_20 | 104 | transition_rampdown_short_sharp1_50 | +| 21 | medium_click1 | 63 | transition_click_10 | 105 | transition_rampdown_short_sharp2_50 | +| 22 | medium_click2_80 | 64 | transition_hum | 106 | transition_rampup_long_smooth1_50 | +| 23 | medium_click3_60 | 65 | transition_hum_80 | 107 | transition_rampup_long_smooth2_50 | +| 24 | sharp_tick1 | 66 | transition_hum_60 | 108 | transition_rampup_med_smooth1_50 | +| 25 | sharp_tick2_80 | 67 | transition_hum_40 | 109 | transition_rampup_med_smooth2_50 | +| 26 | sharp_tick3_60 | 68 | transition_hum_20 | 110 | transition_rampup_short_smooth1_50 | +| 27 | sh_dblclick_str | 69 | transition_hum_10 | 111 | transition_rampup_short_smooth2_50 | +| 28 | sh_dblclick_str_80 | 70 | transition_rampdown_long_smooth1 | 112 | transition_rampup_long_sharp1_50 | +| 29 | sh_dblclick_str_60 | 71 | transition_rampdown_long_smooth2 | 113 | transition_rampup_long_sharp2_50 | +| 30 | sh_dblclick_str_30 | 72 | transition_rampdown_med_smooth1 | 114 | transition_rampup_med_sharp1_50 | +| 31 | sh_dblclick_med | 73 | transition_rampdown_med_smooth2 | 115 | transition_rampup_med_sharp2_50 | +| 32 | sh_dblclick_med_80 | 74 | transition_rampdown_short_smooth1 | 116 | transition_rampup_short_sharp1_50 | +| 33 | sh_dblclick_med_60 | 75 | transition_rampdown_short_smooth2 | 117 | transition_rampup_short_sharp2_50 | +| 34 | sh_dblsharp_tick | 76 | transition_rampdown_long_sharp1 | 118 | long_buzz_for_programmatic_stopping | +| 35 | sh_dblsharp_tick_80 | 77 | transition_rampdown_long_sharp2 | 119 | smooth_hum1_50 | +| 36 | sh_dblsharp_tick_60 | 78 | transition_rampdown_med_sharp1 | 120 | smooth_hum2_40 | +| 37 | lg_dblclick_str | 79 | transition_rampdown_med_sharp2 | 121 | smooth_hum3_30 | +| 38 | lg_dblclick_str_80 | 80 | transition_rampdown_short_sharp1 | 122 | smooth_hum4_20 | +| 39 | lg_dblclick_str_60 | 81 | transition_rampdown_short_sharp2 | 123 | smooth_hum5_10 | +| 40 | lg_dblclick_str_30 | 82 | transition_rampup_long_smooth1 | | | +| 41 | lg_dblclick_med | 83 | transition_rampup_long_smooth2 | | | +| 42 | lg_dblclick_med_80 | 84 | transition_rampup_med_smooth1 | | | +### Optional DRV2605L defines + +```c +#define DRV2605L_GREETING *sequence name or number* +``` +If haptic feedback is enabled, the keyboard will vibrate to a specific sequence during startup. That can be selected using the following define: + +```c +#define DRV2605L_DEFAULT_MODE *sequence name or number* +``` +This will set what sequence `HF_RST` will set as the active mode. If not defined, mode will be set to 1 when `HF_RST` is pressed. + +### DRV2605L Continuous Haptic Mode + +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, the following keys will not trigger feedback: + +* Usual modifier keys such as Control/Shift/Alt/Gui (For example `KC_LCTL`) +* `MO()` momentary keys. See also [Layers](../feature_layers). +* `LM()` momentary keys with mod active. +* `LT()` layer tap keys, when held to activate a layer. However when tapped, and the key is quickly released, and sends a keycode, haptic feedback is still triggered. +* `TT()` layer tap toggle keys, when held to activate a layer. However when tapped `TAPPING_TOGGLE` times to permanently toggle the layer, on the last tap haptic feedback is still triggered. +* `MT()` mod tap keys, when held to keep a usual modifier key pressed. However when tapped, and the key is quickly released, and sends a keycode, haptic feedback is still triggered. See also [Mod-Tap](../mod_tap). + +### 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. diff --git a/docs/features/hd44780.md b/docs/features/hd44780.md new file mode 100644 index 0000000000..6b4209333b --- /dev/null +++ b/docs/features/hd44780.md @@ -0,0 +1,298 @@ +# HD44780 LCD Driver {#hd44780-lcd-driver} + +## Supported Hardware {#supported-hardware} + +LCD modules using [HD44780U](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) IC or equivalent, communicating in 4-bit mode. + +|Module|Size |Notes | +|------|--------------|---------------------------------| +|1602A |16x2, 5x8 dots| | +|2004A |20x4, 5x8 dots|Untested, not currently supported| + +To run these modules at 3.3V, an additional MAX660 voltage converter IC must be soldered on, along with two 10µF capacitors. See [this page](https://www.codrey.com/electronic-circuits/hack-your-16x2-lcd/) for more details. + +## Usage {#usage} + +Add the following to your `rules.mk`: + +```make +HD44780_ENABLE = yes +``` + +## Basic Configuration {#basic-configuration} + +Add the following to your `config.h`: + +|Define |Default |Description | +|-----------------------|--------------|-----------------------------------------------------------------------------------------------------| +|`HD44780_DATA_PINS` |*Not defined* |(Required) An array of four GPIO pins connected to the display's D4-D7 pins, eg. `{ B1, B3, B2, B6 }`| +|`HD44780_RS_PIN` |*Not defined* |(Required) The GPIO connected to the display's RS pin | +|`HD44780_RW_PIN` |*Not defined* |(Required) The GPIO connected to the display's RW pin | +|`HD44780_E_PIN` |*Not defined* |(Required) The GPIO connected to the display's E pin | +|`HD44780_DISPLAY_COLS` |`16` |The number of visible characters on a single line of the display | +|`HD44780_DISPLAY_LINES`|`2` |The number of visible lines on the display | +|`HD44780_WRAP_LINES` |*Not defined* |If defined, input characters will wrap to the next line | + +## Examples {#examples} + +### Hello World {#example-hello-world} + +Add the following to your `keymap.c`: + +```c +void keyboard_post_init_user(void) { + hd44780_init(true, true); // Show blinking cursor + hd44780_puts_P(PSTR("Hello, world!\n")); +} +``` + +### Custom Character Definition {#example-custom-character} + +Up to eight custom characters can be defined. This data is stored in the Character Generator RAM (CGRAM), and is not persistent across power cycles. + +This example defines the QMK Psi as the first custom character. The first 16 positions in the character set are reserved for the eight custom characters duplicated. + +``` +Byte | 16 8 4 2 1 + 1 | x x x ■ □ ■ □ ■ + 2 | x x x ■ □ ■ □ ■ + 3 | x x x ■ □ ■ □ ■ + 4 | x x x □ ■ ■ ■ □ + 5 | x x x □ □ ■ □ □ + 6 | x x x □ □ ■ □ □ + 7 | x x x □ □ ■ □ □ + 8 | x x x □ □ □ □ □ +``` + +```c +const uint8_t PROGMEM psi[8] = { 0x15, 0x15, 0x15, 0x0E, 0x04, 0x04, 0x04, 0x00 }; + +void keyboard_post_init_user(void) { + hd44780_init(false, false); + hd44780_define_char_P(0, psi); + // Cursor is incremented while defining characters so must be reset + hd44780_home(); + // 0x08 to avoid null terminator + hd44780_puts_P(PSTR("\x08 QMK Firmware")); +} +``` + +## API {#api} + +### `void hd44780_init(bool cursor, bool blink)` {#api-hd44780-init} + +Initialize the display. + +This function should be called only once, before any of the other functions can be called. + +#### Arguments {#api-hd44780-init-arguments} + + - `bool cursor` + Whether to show the cursor. + - `bool blink` + Whether to blink the cursor, if shown. + +--- + +### `void hd44780_clear(void)` {#api-hd44780-clear} + +Clear the display. + +This function is called on init. + +--- + +### `void hd44780_home(void)` {#api-hd44780-home} + +Move the cursor to the home position. + +This function is called on init. + +--- + +### `void hd44780_on(bool cursor, bool blink)` {#api-hd44780-on} + +Turn the display on, and/or set the cursor properties. + +This function is called on init. + +#### Arguments {#api-hd44780-on-arguments} + + - `bool cursor` + Whether to show the cursor. + - `bool blink` + Whether to blink the cursor, if shown. + +--- + +### `void hd44780_off(void)` {#api-hd44780-off} + +Turn the display off. + +--- + +### `void hd44780_set_cursor(uint8_t col, uint8_t line)` {#api-hd44780-set-cursor} + +Move the cursor to the specified position on the display. + +#### Arguments {#api-hd44780-set-cursor-arguments} + + - `uint8_t col` + The column number to move to, from 0 to 15 on 16x2 displays. + - `bool line` + The line number to move to, either 0 or 1 on 16x2 displays. + +--- + +### `void hd44780_putc(char c)` {#api-hd44780-putc} + +Print a character to the display. The newline character `\n` will move the cursor to the start of the next line. + +The exact character shown may depend on the ROM code of your particular display - refer to the datasheet for the full character set. + +#### Arguments {#api-hd44780-putc-arguments} + + - `char c` + The character to print. + +--- + +### `void hd44780_puts(const char *s)` {#api-hd44780-puts} + +Print a string of characters to the display. + +#### Arguments {#api-hd44780-puts-arguments} + + - `const char *s` + The string to print. + +--- + +### `void hd44780_puts_P(const char *s)` {#api-hd44780-puts-p} + +Print a string of characters from PROGMEM to the display. + +On ARM devices, this function is simply an alias of `hd44780_puts()`. + +#### Arguments {#api-hd44780-puts-p-arguments} + + - `const char *s` + The PROGMEM string to print (ie. `PSTR("Hello")`). + +--- + +### `void hd44780_define_char(uint8_t index, uint8_t *data)` {#api-hd44780-define-char} + +Define a custom character. + +#### Arguments {#api-hd44780-define-char-arguments} + + - `uint8_t index` + The index of the custom character to define, from 0 to 7. + - `uint8_t *data` + An array of 8 bytes containing the 5-bit row data of the character, where the first byte is the topmost row, and the least significant bit of each byte is the rightmost column. + +--- + +### `void hd44780_define_char_P(uint8_t index, const uint8_t *data)` {#api-hd44780-define-char-p} + +Define a custom character from PROGMEM. + +On ARM devices, this function is simply an alias of `hd44780_define_char()`. + +#### Arguments {#api-hd44780-define-char-p-arguments} + + - `uint8_t index` + The index of the custom character to define, from 0 to 7. + - `const uint8_t *data` + A PROGMEM array of 8 bytes containing the 5-bit row data of the character, where the first byte is the topmost row, and the least significant bit of each byte is the rightmost column. + +--- + +### `bool hd44780_busy(void)` {#api-hd44780-busy} + +Indicates whether the display is currently processing, and cannot accept instructions. + +#### Return Value {#api-hd44780-busy-arguments} + +`true` if the display is busy. + +--- + +### `void hd44780_write(uint8_t data, bool isData)` {#api-hd44780-write} + +Write a byte to the display. + +#### Arguments {#api-hd44780-write-arguments} + + - `uint8_t data` + The byte to send to the display. + - `bool isData` + Whether the byte is an instruction or character data. + +--- + +### `uint8_t hd44780_read(bool isData)` {#api-hd44780-read} + +Read a byte from the display. + +#### Arguments {#api-hd44780-read-arguments} + + - `bool isData` + Whether to read the current cursor position, or the character at the cursor. + +#### Return Value {#api-hd44780-read-return} + +If `isData` is `true`, the returned byte will be the character at the current DDRAM address. Otherwise, it will be the current DDRAM address and the busy flag. + +--- + +### `void hd44780_command(uint8_t command)` {#api-hd44780-command} + +Send a command to the display. Refer to the datasheet and `hd44780.h` for the valid commands and defines. + +This function waits for the display to clear the busy flag before sending the command. + +#### Arguments {#api-hd44780-command-arguments} + + - `uint8_t command` + The command to send. + +--- + +### `void hd44780_data(uint8_t data)` {#api-hd44780-data} + +Send a byte of data to the display. + +This function waits for the display to clear the busy flag before sending the data. + +#### Arguments {#api-hd44780-data-arguments} + + - `uint8_t data` + The byte of data to send. + +--- + +### `void hd44780_set_cgram_address(uint8_t address)` {#api-hd44780-set-cgram-address} + +Set the CGRAM address. + +This function is used when defining custom characters. + +#### Arguments {#api-hd44780-set-cgram-address-arguments} + + - `uint8_t address` + The CGRAM address to move to, from `0x00` to `0x3F`. + +--- + +### `void hd44780_set_ddram_address(uint8_t address)` {#api-hd44780-set-ddram-address} + +Set the DDRAM address. + +This function is used when printing characters to the display, and setting the cursor. + +#### Arguments {#api-hd44780-set-ddram-address-arguments} + + - `uint8_t address` + The DDRAM address to move to, from `0x00` to `0x7F`. diff --git a/docs/features/joystick.md b/docs/features/joystick.md new file mode 100644 index 0000000000..f3fd209d5a --- /dev/null +++ b/docs/features/joystick.md @@ -0,0 +1,222 @@ +# Joystick {#joystick} + +This feature provides game controller input as a joystick device supporting up to 6 axes and 32 buttons. Axes can be read either from an [ADC-capable input pin](../drivers/adc), or can be virtual, so that its value is provided by your code. + +An analog device such as a [potentiometer](https://en.wikipedia.org/wiki/Potentiometer) found on an analog joystick's axes is based on a voltage divider, where adjusting the movable wiper controls the output voltage which can then be read by the microcontroller's ADC. + +## Usage {#usage} + +Add the following to your `rules.mk`: + +```make +JOYSTICK_ENABLE = yes +``` + +By default the joystick driver is `analog`, but you can change this with: + +```make +JOYSTICK_DRIVER = digital +``` + +## Configuration {#configuration} + +By default, two axes and eight buttons are defined, with a reported resolution of 8 bits (-127 to +127). This can be changed in your `config.h`: + +```c +// Min 0, max 32 +#define JOYSTICK_BUTTON_COUNT 16 +// Min 0, max 6: X, Y, Z, Rx, Ry, Rz +#define JOYSTICK_AXIS_COUNT 3 +// Min 8, max 16 +#define JOYSTICK_AXIS_RESOLUTION 10 +``` + +::: tip +You must define at least one button or axis. Also note that the maximum ADC resolution of the supported AVR MCUs is 10-bit, and 12-bit for most STM32 MCUs. +::: + +### Axes {#axes} + +When defining axes for your joystick, you must provide a definition array typically in your `keymap.c`. + +For instance, the below example configures two axes. The X axis is read from the `A4` pin. With the default axis resolution of 8 bits, the range of values between 900 and 575 are scaled to -127 through 0, and values 575 to 285 are scaled to 0 through 127. The Y axis is configured as a virtual axis, and its value is not read from any pin. Instead, the user must update the axis value programmatically. + +```c +joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = { + JOYSTICK_AXIS_IN(A4, 900, 575, 285), + JOYSTICK_AXIS_VIRTUAL +}; +``` + +Axes can be configured using one of the following macros: + + * `JOYSTICK_AXIS_IN(input_pin, low, rest, high)` + The ADC samples the provided pin. `low`, `high` and `rest` correspond to the minimum, maximum, and resting (or centered) analog values of the axis, respectively. + * `JOYSTICK_AXIS_VIRTUAL` + No ADC reading is performed. The value should be provided by user code. + +The `low` and `high` values can be swapped to effectively invert the axis. + +#### Virtual Axes {#virtual-axes} + +The following example adjusts two virtual axes (X and Y) based on keypad presses, with `KC_P0` as a precision modifier: + +```c +joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = { + JOYSTICK_AXIS_VIRTUAL, // x + JOYSTICK_AXIS_VIRTUAL // y +}; + +static bool precision = false; +static uint16_t precision_mod = 64; +static uint16_t axis_val = 127; + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + int16_t precision_val = axis_val; + if (precision) { + precision_val -= precision_mod; + } + + switch (keycode) { + case KC_P8: + joystick_set_axis(1, record->event.pressed ? -precision_val : 0); + return false; + case KC_P2: + joystick_set_axis(1, record->event.pressed ? precision_val : 0); + return false; + case KC_P4: + joystick_set_axis(0, record->event.pressed ? -precision_val : 0); + return false; + case KC_P6: + joystick_set_axis(0, record->event.pressed ? precision_val : 0); + return false; + case KC_P0: + precision = record->event.pressed; + return false; + } + return true; +} +``` + +## Keycodes {#keycodes} + +|Key |Aliases|Description| +|-----------------------|-------|-----------| +|`QK_JOYSTICK_BUTTON_0` |`JS_0` |Button 0 | +|`QK_JOYSTICK_BUTTON_1` |`JS_1` |Button 1 | +|`QK_JOYSTICK_BUTTON_2` |`JS_2` |Button 2 | +|`QK_JOYSTICK_BUTTON_3` |`JS_3` |Button 3 | +|`QK_JOYSTICK_BUTTON_4` |`JS_4` |Button 4 | +|`QK_JOYSTICK_BUTTON_5` |`JS_5` |Button 5 | +|`QK_JOYSTICK_BUTTON_6` |`JS_6` |Button 6 | +|`QK_JOYSTICK_BUTTON_7` |`JS_7` |Button 7 | +|`QK_JOYSTICK_BUTTON_8` |`JS_8` |Button 8 | +|`QK_JOYSTICK_BUTTON_9` |`JS_9` |Button 9 | +|`QK_JOYSTICK_BUTTON_10`|`JS_10`|Button 10 | +|`QK_JOYSTICK_BUTTON_11`|`JS_11`|Button 11 | +|`QK_JOYSTICK_BUTTON_12`|`JS_12`|Button 12 | +|`QK_JOYSTICK_BUTTON_13`|`JS_13`|Button 13 | +|`QK_JOYSTICK_BUTTON_14`|`JS_14`|Button 14 | +|`QK_JOYSTICK_BUTTON_15`|`JS_15`|Button 15 | +|`QK_JOYSTICK_BUTTON_16`|`JS_16`|Button 16 | +|`QK_JOYSTICK_BUTTON_17`|`JS_17`|Button 17 | +|`QK_JOYSTICK_BUTTON_18`|`JS_18`|Button 18 | +|`QK_JOYSTICK_BUTTON_19`|`JS_19`|Button 19 | +|`QK_JOYSTICK_BUTTON_20`|`JS_20`|Button 20 | +|`QK_JOYSTICK_BUTTON_21`|`JS_21`|Button 21 | +|`QK_JOYSTICK_BUTTON_22`|`JS_22`|Button 22 | +|`QK_JOYSTICK_BUTTON_23`|`JS_23`|Button 23 | +|`QK_JOYSTICK_BUTTON_24`|`JS_24`|Button 24 | +|`QK_JOYSTICK_BUTTON_25`|`JS_25`|Button 25 | +|`QK_JOYSTICK_BUTTON_26`|`JS_26`|Button 26 | +|`QK_JOYSTICK_BUTTON_27`|`JS_27`|Button 27 | +|`QK_JOYSTICK_BUTTON_28`|`JS_28`|Button 28 | +|`QK_JOYSTICK_BUTTON_29`|`JS_29`|Button 29 | +|`QK_JOYSTICK_BUTTON_30`|`JS_30`|Button 30 | +|`QK_JOYSTICK_BUTTON_31`|`JS_31`|Button 31 | + +## API {#api} + +### `struct joystick_t` {#api-joystick-t} + +Contains the state of the joystick. + +#### Members {#api-joystick-t-members} + + - `uint8_t buttons[]` + A bit-packed array containing the joystick button states. The size is calculated as `(JOYSTICK_BUTTON_COUNT - 1) / 8 + 1`. + - `int16_t axes[]` + An array of analog values for each defined axis. + - `bool dirty` + Whether the current state needs to be sent to the host. + +--- + +### `struct joystick_config_t` {#api-joystick-config-t} + +Describes a single axis. + +#### Members {#api-joystick-config-t-members} + + - `pin_t input_pin` + The pin to read the analog value from, or `JS_VIRTUAL_AXIS`. + - `uint16_t min_digit` + The minimum analog value. + - `uint16_t mid_digit` + The resting or midpoint analog value. + - `uint16_t max_digit` + The maximum analog value. + +--- + +### `void joystick_flush(void)` {#api-joystick-flush} + +Send the joystick report to the host, if it has been marked as dirty. + +--- + +### `void register_joystick_button(uint8_t button)` {#api-register-joystick-button} + +Set the state of a button, and flush the report. + +#### Arguments {#api-register-joystick-button-arguments} + + - `uint8_t button` + The index of the button to press, from 0 to 31. + +--- + +### `void unregister_joystick_button(uint8_t button)` {#api-unregister-joystick-button} + +Reset the state of a button, and flush the report. + +#### Arguments {#api-unregister-joystick-button-arguments} + + - `uint8_t button` + The index of the button to release, from 0 to 31. + +--- + +### `int16_t joystick_read_axis(uint8_t axis)` {#api-joystick-read-axis} + +Sample and process the analog value of the given axis. + +#### Arguments {#api-joystick-read-axis-arguments} + + - `uint8_t axis` + The axis to read. + +#### Return Value {#api-joystick-read-axis-return} + +A signed 16-bit integer, where 0 is the resting or mid point. + +### `void joystick_set_axis(uint8_t axis, int16_t value)` {#api-joystick-set-axis} + +Set the value of the given axis. + +#### Arguments {#api-joystick-set-axis-arguments} + + - `uint8_t axis` + The axis to set the value of. + - `int16_t value` + The value to set. diff --git a/docs/features/key_lock.md b/docs/features/key_lock.md new file mode 100644 index 0000000000..ba3b71ef06 --- /dev/null +++ b/docs/features/key_lock.md @@ -0,0 +1,23 @@ +# Key Lock + +Sometimes you may find yourself needing to hold down a specific key for a long period of time. Key Lock holds down the next key you press for you. Press it again, and it will be released. + +Let's say you need to type in ALL CAPS for a few sentences. Hit `QK_LOCK`, and then Shift. Now, Shift will be considered held until you tap it again. You can think of Key Lock as Caps Lock, but supercharged. + +## Usage + +First, enable Key Lock by setting `KEY_LOCK_ENABLE = yes` in your `rules.mk`. Then pick a key in your keymap and assign it the keycode `QK_LOCK`. + +## Keycodes + +|Keycode |Description | +|---------|--------------------------------------------------------------| +|`QK_LOCK`|Hold down the next key pressed, until the key is pressed again| + +## Caveats + +Key Lock is only able to hold standard action keys and [One Shot modifier](../one_shot_keys) keys (for example, if you have your Shift defined as `OSM(MOD_LSFT)`). +This does not include any of the QMK special functions (except One Shot modifiers), or shifted versions of keys such as `KC_LPRN`. If it's in the [Basic Keycodes](../keycodes_basic) list, it can be held. + +Switching layers will not cancel the Key Lock. The Key Lock can be cancelled by calling the `cancel_key_lock()` function. + diff --git a/docs/features/key_overrides.md b/docs/features/key_overrides.md new file mode 100644 index 0000000000..4c568f1679 --- /dev/null +++ b/docs/features/key_overrides.md @@ -0,0 +1,256 @@ +# Key Overrides {#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 {#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 {#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-modifier 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 {#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_BSPC, KC_DEL); + +// 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 {#intermediate-difficulty-examples} + +### Media Controls & Screen Brightness {#media-controls-amp-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 {#flexible-macos-friendly-grave-escape} +The [Grave Escape feature](grave_esc) is limited in its configurability and has [bugs when used on macOS](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_GRV)); + +// GUI + esc = ` +const key_override_t grave_esc_override = ko_make_basic(MOD_MASK_GUI, KC_ESC, KC_GRV); + +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 {#advanced-examples} +### Modifiers as Layer Keys {#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 {#keycodes} + +|Keycode |Aliases |Description | +|------------------------|---------|----------------------| +|`QK_KEY_OVERRIDE_TOGGLE`|`KO_TOGG`|Toggle key overrides | +|`QK_KEY_OVERRIDE_ON` |`KO_ON` |Turn on key overrides | +|`QK_KEY_OVERRIDE_OFF` |`KO_OFF` |Turn off key overrides| + +## Reference for `key_override_t` {#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` {#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 {#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 {#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 {#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 {#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 {#difference-to-combos} + +Note that key overrides are very different from [combos](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 interaction with other pressed keys. There are a number of optional settings that can be used to really fine-tune the behavior of each key override as well. Using key overrides also does not delay key input for regular key presses, which inherently happens in combos and may be undesirable. + +## Solution to the problem of flashing modifiers {#neutralize-flashing-modifiers} + +If the programs you use bind an action to taps of modifier keys (e.g. tapping left GUI to bring up the applications menu or tapping left Alt to focus the menu bar), you may find that using key overrides with suppressed mods falsely triggers those actions. To counteract this, you can define a `DUMMY_MOD_NEUTRALIZER_KEYCODE` in `config.h` that will get sent in between the register and unregister events of a suppressed modifier. That way, the programs on your computer will no longer interpret the mod suppression induced by key overrides as a lone tap of a modifier key and will thus not falsely trigger the undesired action. + +Naturally, for this technique to be effective, you must choose a `DUMMY_MOD_NEUTRALIZER_KEYCODE` for which no keyboard shortcuts are bound to. Recommended values are: `KC_RIGHT_CTRL` or `KC_F18`. +Please note that `DUMMY_MOD_NEUTRALIZER_KEYCODE` must be a basic, unmodified, HID keycode so values like `KC_NO`, `KC_TRANSPARENT` or `KC_PIPE` aka `S(KC_BACKSLASH)` are not permitted. + +By default, only left Alt and left GUI are neutralized. If you want to change the list of applicable modifier masks, use the following in your `config.h`: + +```c +#define MODS_TO_NEUTRALIZE { , , ... } +``` + +Examples: + +```c +#define DUMMY_MOD_NEUTRALIZER_KEYCODE KC_RIGHT_CTRL + +// Neutralize left alt and left GUI (Default value) +#define MODS_TO_NEUTRALIZE { MOD_BIT(KC_LEFT_ALT), MOD_BIT(KC_LEFT_GUI) } + +// Neutralize left alt, left GUI, right GUI and left Control+Shift +#define MODS_TO_NEUTRALIZE { MOD_BIT(KC_LEFT_ALT), MOD_BIT(KC_LEFT_GUI), MOD_BIT(KC_RIGHT_GUI), MOD_BIT(KC_LEFT_CTRL)|MOD_BIT(KC_LEFT_SHIFT) } +``` + +::: warning +Do not use `MOD_xxx` constants like `MOD_LSFT` or `MOD_RALT`, since they're 5-bit packed bit-arrays while `MODS_TO_NEUTRALIZE` expects a list of 8-bit packed bit-arrays. Use `MOD_BIT()` or `MOD_MASK_xxx` instead. +::: diff --git a/docs/features/leader_key.md b/docs/features/leader_key.md new file mode 100644 index 0000000000..a36e630a36 --- /dev/null +++ b/docs/features/leader_key.md @@ -0,0 +1,297 @@ +# The Leader Key: A New Kind of Modifier {#the-leader-key} + +If you're a Vim user, you probably know what a Leader key is. In contrast to [Combos](combo), the Leader key allows you to hit a *sequence* of up to five keys instead, which triggers some custom functionality once complete. + +## Usage {#usage} + +Add the following to your `rules.mk`: + +```make +LEADER_ENABLE = yes +``` + +Then add the `QK_LEAD` keycode to your keymap. + +## Callbacks {#callbacks} + +These callbacks are invoked when the leader sequence begins and ends. In the latter you can implement your custom functionality based on the contents of the sequence buffer. + +```c +void leader_start_user(void) { + // Do something when the leader key is pressed +} + +void leader_end_user(void) { + if (leader_sequence_one_key(KC_F)) { + // Leader, f => Types the below string + SEND_STRING("QMK is awesome."); + } else if (leader_sequence_two_keys(KC_D, KC_D)) { + // Leader, d, d => Ctrl+A, Ctrl+C + SEND_STRING(SS_LCTL("a") SS_LCTL("c")); + } else if (leader_sequence_three_keys(KC_D, KC_D, KC_S)) { + // Leader, d, d, s => Types the below string + SEND_STRING("https://start.duckduckgo.com\n"); + } else if (leader_sequence_two_keys(KC_A, KC_S)) { + // Leader, a, s => GUI+S + tap_code16(LGUI(KC_S)); + } +} +``` + +## Basic Configuration {#basic-configuration} + +### Timeout {#timeout} + +This is the amount of time you have to complete a sequence once the leader key has been pressed. The default value is 300 milliseconds, but you can change this by adding the following to your `config.h`: + +```c +#define LEADER_TIMEOUT 350 +``` + +### Per-Key Timeout {#per-key-timeout} + +Rather than relying on an incredibly high timeout for long leader key strings or those of us without 200 wpm typing skills, you can enable per-key timing to ensure that each key pressed provides you with more time to finish the sequence. This is incredibly helpful with leader key emulation of tap dance (such as multiple taps of the same key like C, C, C). + +To enable this, add the following to your `config.h`: + +```c +#define LEADER_PER_KEY_TIMING +``` + +After this, it's recommended that you lower your timeout below 300 ms: + +```c +#define LEADER_TIMEOUT 250 +``` + +Now, something like this won't seem impossible to do without a 1000 millisecond timeout: + +```c +if (leader_sequence_three_keys(KC_C, KC_C, KC_C)) { + SEND_STRING("Per key timing is great!!!"); +} +``` + +### Disabling Initial Timeout {#disabling-initial-timeout} + +Sometimes your leader key may be too far away from the rest of the keys in the sequence. Imagine that your leader key is one of your outer top right keys - you may need to reposition your hand just to reach your leader key. This can make typing the entire sequence on time hard difficult if you are able to type most of the sequence fast. For example, if your sequence is `Leader + asd`, typing `asd` fast is very easy once you have your hands in your home row, but starting the sequence in time after moving your hand out of the home row to reach the leader key and back is not. + +To remove the stress this situation produces to your hands, you can disable the timeout just for the leader key. Add the following to your `config.h`: + +```c +#define LEADER_NO_TIMEOUT +``` + +Now, after you hit the leader key, you will have an infinite amount of time to start the rest of the sequence, allowing you to properly position your hands to type the rest of the sequence comfortably. This way you can configure a very short `LEADER_TIMEOUT`, but still have plenty of time to position your hands. + +### Strict Key Processing {#strict-key-processing} + +By default, only the "tap keycode" portions of [Mod-Taps](../mod_tap) and [Layer Taps](../feature_layers#switching-and-toggling-layers) are added to the sequence buffer. This means if you press eg. `LT(3, KC_A)` as part of a sequence, `KC_A` will be added to the buffer, rather than the entire `LT(3, KC_A)` keycode. + +This gives a more expected behaviour for most users, however you may want to change this. + +To enable this, add the following to your `config.h`: + +```c +#define LEADER_KEY_STRICT_KEY_PROCESSING +``` + +## Example {#example} + +This example will play the Mario "One Up" sound when you hit `QK_LEAD` to start the leader sequence. When the sequence ends, it will play "All Star" if it completes successfully or "Rick Roll" you if it fails (in other words, no sequence matched). + +```c +#ifdef AUDIO_ENABLE +float leader_start_song[][2] = SONG(ONE_UP_SOUND); +float leader_succeed_song[][2] = SONG(ALL_STAR); +float leader_fail_song[][2] = SONG(RICK_ROLL); +#endif + +void leader_start_user(void) { +#ifdef AUDIO_ENABLE + PLAY_SONG(leader_start_song); +#endif +} + +void leader_end_user(void) { + bool did_leader_succeed = false; + + if (leader_sequence_one_key(KC_E)) { + SEND_STRING(SS_LCTL(SS_LSFT("t"))); + did_leader_succeed = true; + } else if (leader_sequence_two_keys(KC_E, KC_D)) { + SEND_STRING(SS_LGUI("r") "cmd\n" SS_LCTL("c")); + did_leader_succeed = true; + } + +#ifdef AUDIO_ENABLE + if (did_leader_succeed) { + PLAY_SONG(leader_succeed_song); + } else { + PLAY_SONG(leader_fail_song); + } +#endif +} +``` + +## Keycodes {#keycodes} + +|Key |Aliases |Description | +|-----------------------|---------|-------------------------| +|`QK_LEADER` |`QK_LEAD`|Begin the leader sequence| + +## API {#api} + +### `void leader_start_user(void)` {#api-leader-start-user} + +User callback, invoked when the leader sequence begins. + +--- + +### `void leader_end_user(void)` {#api-leader-end-user} + +User callback, invoked when the leader sequence ends. + +--- + +### `void leader_start(void)` {#api-leader-start} + +Begin the leader sequence, resetting the buffer and timer. + +--- + +### `void leader_end(void)` {#api-leader-end} + +End the leader sequence. + +--- + +### `bool leader_sequence_active(void)` {#api-leader-sequence-active} + +Whether the leader sequence is active. + +--- + +### `bool leader_sequence_add(uint16_t keycode)` {#api-leader-sequence-add} + +Add the given keycode to the sequence buffer. + +If `LEADER_NO_TIMEOUT` is defined, the timer is reset if the buffer is empty. + +#### Arguments {#api-leader-sequence-add-arguments} + + - `uint16_t keycode` + The keycode to add. + +#### Return Value {#api-leader-sequence-add-return} + +`true` if the keycode was added, `false` if the buffer is full. + +--- + +### `bool leader_sequence_timed_out(void)` {#api-leader-sequence-timed-out} + +Whether the leader sequence has reached the timeout. + +If `LEADER_NO_TIMEOUT` is defined, the buffer must also contain at least one key. + +--- + +### `bool leader_reset_timer(void)` {#api-leader-reset-timer} + +Reset the leader sequence timer. + +--- + +### `bool leader_sequence_one_key(uint16_t kc)` {#api-leader-sequence-one-key} + +Check the sequence buffer for the given keycode. + +#### Arguments {#api-leader-sequence-one-key-arguments} + + - `uint16_t kc` + The keycode to check. + +#### Return Value {#api-leader-sequence-one-key-return} + +`true` if the sequence buffer matches. + +--- + +### `bool leader_sequence_two_keys(uint16_t kc1, uint16_t kc2)` {#api-leader-sequence-two-keys} + +Check the sequence buffer for the given keycodes. + +#### Arguments {#api-leader-sequence-two-keys-arguments} + + - `uint16_t kc1` + The first keycode to check. + - `uint16_t kc2` + The second keycode to check. + +#### Return Value {#api-leader-sequence-two-keys-return} + +`true` if the sequence buffer matches. + +--- + +### `bool leader_sequence_three_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3)` {#api-leader-sequence-three-keys} + +Check the sequence buffer for the given keycodes. + +#### Arguments {#api-leader-sequence-three-keys-arguments} + + - `uint16_t kc1` + The first keycode to check. + - `uint16_t kc2` + The second keycode to check. + - `uint16_t kc3` + The third keycode to check. + +#### Return Value {#api-leader-sequence-three-keys-return} + +`true` if the sequence buffer matches. + +--- + +### `bool leader_sequence_four_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3, uint16_t kc4)` {#api-leader-sequence-four-keys} + +Check the sequence buffer for the given keycodes. + +#### Arguments {#api-leader-sequence-four-keys-arguments} + + - `uint16_t kc1` + The first keycode to check. + - `uint16_t kc2` + The second keycode to check. + - `uint16_t kc3` + The third keycode to check. + - `uint16_t kc4` + The fourth keycode to check. + +#### Return Value {#api-leader-sequence-four-keys-return} + +`true` if the sequence buffer matches. + +--- + +### `bool leader_sequence_five_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3, uint16_t kc4, uint16_t kc5)` {#api-leader-sequence-five-keys} + +Check the sequence buffer for the given keycodes. + +#### Arguments {#api-leader-sequence-five-keys-arguments} + + - `uint16_t kc1` + The first keycode to check. + - `uint16_t kc2` + The second keycode to check. + - `uint16_t kc3` + The third keycode to check. + - `uint16_t kc4` + The fourth keycode to check. + - `uint16_t kc5` + The fifth keycode to check. + +#### Return Value {#api-leader-sequence-five-keys-return} + +`true` if the sequence buffer matches. diff --git a/docs/features/led_indicators.md b/docs/features/led_indicators.md new file mode 100644 index 0000000000..8435c69a55 --- /dev/null +++ b/docs/features/led_indicators.md @@ -0,0 +1,127 @@ +# LED Indicators + +::: tip +LED indicators on split keyboards will require state information synced to the slave half (e.g. `#define SPLIT_LED_STATE_ENABLE`). See [data sync options](split_keyboard#data-sync-options) for more details. +::: + +QMK provides methods to read 5 of the LEDs defined in the HID spec: + +* Num Lock +* Caps Lock +* Scroll Lock +* Compose +* Kana + +There are three ways to get the lock LED state: +* Configuration options in `config.h` +* Implement `led_update_*` function +* Call `led_t host_keyboard_led_state()` + +::: warning +The `host_keyboard_led_state()` may reflect an updated state before `led_update_user()` is called. +::: + +Two deprecated functions that provide the LED state as `uint8_t`: + +* `uint8_t led_set_user(uint8_t usb_led)` +* `uint8_t host_keyboard_leds()` + +## Configuration Options + +To configure the indicators, `#define` these in your `config.h`: + +|Define |Default |Description | +|---------------------|-------------|-------------------------------------------| +|`LED_NUM_LOCK_PIN` |*Not defined*|The pin that controls the `Num Lock` LED | +|`LED_CAPS_LOCK_PIN` |*Not defined*|The pin that controls the `Caps Lock` LED | +|`LED_SCROLL_LOCK_PIN`|*Not defined*|The pin that controls the `Scroll Lock` LED| +|`LED_COMPOSE_PIN` |*Not defined*|The pin that controls the `Compose` LED | +|`LED_KANA_PIN` |*Not defined*|The pin that controls the `Kana` LED | +|`LED_PIN_ON_STATE` |`1` |The state of the indicator pins when the LED is "on" - `1` for high, `0` for low| + +Unless you are designing your own keyboard, you generally should not need to change the above config options. + +## LED update function + +When the configuration options do not provide enough flexibility, the following callbacks allow custom control of the LED behavior. These functions will be called when one of those 5 LEDs changes state: + +* Keyboard/revision: `bool led_update_kb(led_t led_state)` +* Keymap: `bool led_update_user(led_t led_state)` + +Both receives LED state as a struct parameter. Returning `true` in `led_update_user()` will allow the keyboard level code in `led_update_kb()` to run as well. Returning `false` will override the keyboard level code, depending on how the keyboard level function is set up. + +::: tip +This boolean return type of `led_update_user` allows for overriding keyboard LED controls, and is thus recommended over the void `led_set_user` function. +::: + +### Example of keyboard LED update implementation + +This is a template indicator function that can be implemented on keyboard level code: + +```c +bool led_update_kb(led_t led_state) { + bool res = led_update_user(led_state); + if(res) { + // gpio_write_pin sets the pin high for 1 and low for 0. + // In this example the pins are inverted, setting + // it low/0 turns it on, and high/1 turns the LED off. + // This behavior depends on whether the LED is between the pin + // and VCC or the pin and GND. + gpio_write_pin(B0, !led_state.num_lock); + gpio_write_pin(B1, !led_state.caps_lock); + gpio_write_pin(B2, !led_state.scroll_lock); + gpio_write_pin(B3, !led_state.compose); + gpio_write_pin(B4, !led_state.kana); + } + return res; +} +``` + +### Example of user LED update implementation + +This is an incomplete example will play a sound if Caps Lock is turned on or off. It returns `true` to allow keyboard LED function to maintain their state. + +```c +#ifdef AUDIO_ENABLE + float caps_on[][2] = SONG(CAPS_LOCK_ON_SOUND); + float caps_off[][2] = SONG(CAPS_LOCK_OFF_SOUND); +#endif + +bool led_update_user(led_t led_state) { + #ifdef AUDIO_ENABLE + static uint8_t caps_state = 0; + if (caps_state != led_state.caps_lock) { + led_state.caps_lock ? PLAY_SONG(caps_on) : PLAY_SONG(caps_off); + caps_state = led_state.caps_lock; + } + #endif + return true; +} +``` + +## Host keyboard LED state + +The `host_keyboard_led_state()` function will report the LED state returned from the host computer as `led_t`. This is useful for reading the LED state outside `led_update_*`. For example, you can get the boolean state of Caps Lock from the host with: + +```c +bool caps = host_keyboard_led_state().caps_lock; +``` + +## `led_update_ports()` + +This function writes the LED state to the actual hardware. Call it manually +from your `led_update_*()` callbacks to modify the handling of the standard +keyboard LEDs. +For example when repurposing a standard LED indicator as layer indicator. + +## Setting Physical LED State + +Some keyboard implementations provide convenient methods for setting the state of the physical LEDs. + +### Ergodox Boards + +The Ergodox implementations provide `ergodox_right_led_1`/`2`/`3_on`/`off()` to turn individual LEDs on or off, as well as `ergodox_right_led_on`/`off(uint8_t led)` to turn them on or off by their index. + +In addition, it is possible to specify the brightness level of all LEDs with `ergodox_led_all_set(uint8_t n)`; of individual LEDs with `ergodox_right_led_1`/`2`/`3_set(uint8_t n)`; or by index with `ergodox_right_led_set(uint8_t led, uint8_t n)`. + +Ergodox boards also define `LED_BRIGHTNESS_LO` for the lowest brightness and `LED_BRIGHTNESS_HI` for the highest brightness (which is the default). diff --git a/docs/features/led_matrix.md b/docs/features/led_matrix.md new file mode 100644 index 0000000000..fee7b139bc --- /dev/null +++ b/docs/features/led_matrix.md @@ -0,0 +1,455 @@ +# LED Matrix Lighting {#led-matrix-lighting} + +This feature allows you to use LED matrices driven by external drivers. It hooks into the backlight system so you can use the same keycodes as backlighting to control it. + +If you want to use RGB LED's you should use the [RGB Matrix Subsystem](rgb_matrix) instead. + +## Driver configuration {#driver-configuration} +--- +### IS31FL3731 {#is31fl3731} + +There is basic support for addressable LED matrix lighting with the I2C IS31FL3731 LED controller. To enable it, add this to your `rules.mk`: + +```make +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`: + +| Variable | Description | Default | +|----------|-------------|---------| +| `IS31FL3731_I2C_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 | +| `IS31FL3731_I2C_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 | +| `LED_MATRIX_LED_COUNT` | (Required) How many LED lights are present across all drivers | | +| `IS31FL3731_I2C_ADDRESS_1` | (Required) Address for the first LED driver | | +| `IS31FL3731_I2C_ADDRESS_2` | (Optional) Address for the second LED driver | | +| `IS31FL3731_I2C_ADDRESS_3` | (Optional) Address for the third LED driver | | +| `IS31FL3731_I2C_ADDRESS_4` | (Optional) Address for the fourth LED driver | | + +Here is an example using 2 drivers. + +```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) +// The address will vary depending on your wiring: +// 00 AD <-> GND +// 01 AD <-> SCL +// 10 AD <-> SDA +// 11 AD <-> VCC +// ADDR represents A1:A0 of the 7-bit address. +// The result is: 0b11101(ADDR) +#define IS31FL3731_I2C_ADDRESS_1 IS31FL3731_I2C_ADDRESS_GND +#define IS31FL3731_I2C_ADDRESS_2 IS31FL3731_I2C_ADDRESS_SDA + +#define LED_DRIVER_1_LED_TOTAL 25 +#define LED_DRIVER_2_LED_TOTAL 24 +#define LED_MATRIX_LED_COUNT (LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL) +``` + +::: warning +Note the parentheses, this is so when `LED_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL)` will give very different results than `rand() % LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL`. +::: + +For split keyboards using `LED_MATRIX_SPLIT` with an LED driver, you can either have the same driver address or different driver addresses. If using different addresses, use `IS31FL3731_I2C_ADDRESS_1` for one and `IS31FL3731_I2C_ADDRESS_2` for the other one. Then, in `g_is31fl3731_leds`, fill out the correct driver index (0 or 1). If using one address, use `IS31FL3731_I2C_ADDRESS_1` for both, and use index 0 for `g_is31fl3731_leds`. + +Define these arrays listing all the LEDs in your `.c`: + +```c +const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT] = { +/* Refer to IS31 manual for these locations + * driver + * | LED address + * | | */ + { 0, C1_1 }, + { 0, C1_15 }, + // ... +} +``` + +Where `Cx_y` is the location of the LED in the matrix defined by [the datasheet](https://www.issi.com/WW/pdf/31FL3731.pdf) and the header file `drivers/led/issi/is31fl3731-mono.h`. The `driver` is the index of the driver you defined in your `config.h` (`0`, `1`, `2`, or `3` ). + +--- +### IS31FLCOMMON {#is31flcommon} + +There is basic support for addressable LED matrix lighting with a selection of I2C ISSI Lumissil LED controllers through a shared common driver. To enable it, add this to your `rules.mk`: + +```makefile +LED_MATRIX_ENABLE = yes +LED_MATRIX_DRIVER = +``` + +Where `` is the applicable LED driver chip as below + +| Driver Name | Data Sheet | Capability | +|-------------|------------|------------| +| `IS31FL3742A` | [datasheet](https://www.lumissil.com/assets/pdf/core/IS31FL3742A_DS.pdf) | 180 LED, 30x6 Matrix | +| `IS31FL3743A` | [datasheet](https://www.lumissil.com/assets/pdf/core/IS31FL3743A_DS.pdf) | 198 LED, 18x11 Matrix | +| `IS31FL3745` | [datasheet](https://www.lumissil.com/assets/pdf/core/IS31FL3745_DS.pdf) | 144 LED, 18x8 Matrix | +| `IS31FL3746A` | [datasheet](https://www.lumissil.com/assets/pdf/core/IS31FL3746A_DS.pdf) | 72 LED, 18x4 Matrix | + +You can use between 1 and 4 IC's. Do not specify `DRIVER_ADDR_` define for IC's if not present on your keyboard. The `DRIVER_ADDR_1` default assumes that all Address pins on the controller have been connected to GND. Drivers that have SYNC functionality have the default settings to disable if 1 driver. If more than 1 drivers then `DRIVER_ADDR_1` will be set to Master and the remaiing ones set to Slave. + +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 | +| `LED_MATRIX_LED_COUNT` | (Required) How many LED lights are present across all drivers | | +| `DRIVER_ADDR_1` | (Optional) Address for the first LED driver | | +| `DRIVER_ADDR_` | (Required) Address for the additional LED drivers | | +| `ISSI_SSR_` | (Optional) Configuration for the Spread Spectrum Register | | +| `ISSI_CONFIGURATION` | (Optional) Configuration for the Configuration Register | | +| `ISSI_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF | +| `ISSI_PULLDOWNUP` | (Optional) Configuration for the Pull Up & Pull Down Register | | +| `ISSI_TEMP` | (Optional) Configuration for the Tempature Register | | +| `ISSI_PWM_ENABLE` | (Optional) Configuration for the PWM Enable Register | | +| `ISSI_PWM_SET` | (Optional) Configuration for the PWM Setting Register | | +| `ISSI_SCAL_LED ` | (Optional) Configuration for the LEDs Scaling Registers | 0xFF | +| `ISSI_MANUAL_SCALING` | (Optional) If you wish to configure the Scaling Registers manually | | + + +Defaults + +| Variable | IS31FL3742A | IS31FL3743A | IS31FL3745 | IS31FL3746 | +|----------|-------------|-------------|------------|------------| +| `DRIVER_ADDR_1` | 0b0110000 | 0b0100000 | 0b0100000 | 0b1100000 | +| `ISSI_SSR_1` | 0x00 | 0x00 / 0x60 | 0x00 / 0xC0 | 0x00 | +| `ISSI_SSR_<2-4>` | 0x00 | 0x40 | 0x80 | 0x00 | +| `ISSI_CONFIGURATION` | 0x31 | 0x01 | 0x31 | 0x01 | +| `ISSI_PULLDOWNUP` | 0x55 | 0x33 | 0x33 | 0x33 | +| `ISSI_TEMP` | N/A | 0x00 | 0x00 | 0x00 | +| `ISSI_PWM_ENABLE` | N/A | N/A | N/A | 0x00 | +| `ISSI_PWM_SET` | 0x00 | N/A | N/A | 0x00 | + +Here is an example using 2 drivers. + +```c +#define DRIVER_ADDR_2 0b0100001 + +#define DRIVER_1_LED_TOTAL 66 +#define DRIVER_2_LED_TOTAL 42 +#define LED_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL) +``` +::: warning +Note the parentheses, this is so when `LED_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (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 4 drivers are supported, but it would be trivial to support for more. Note that using a combination of different drivers is not supported. All drivers must be of the same model. + +Define these arrays listing all the LEDs in your `.c`: + +```c +const is31_led PROGMEM g_is31_leds[LED_MATRIX_LED_COUNT] = { +/* Refer to IS31 manual for these locations + * driver + * | LED address + * | | */ + { 0, SW1_CS1 }, + { 0, SW1_CS2 }, + // ... +} +``` + +Where `CSx_SWx` is the location of the LED in the matrix defined by the datasheet. The `driver` is the index of the driver you defined in your `config.h` (`0`, `1`, `2`, or `3` for now). + +`ISSI_MANUAL_SCALING` is used to override the Scaling for individual LED's. By default they will be set as per `ISSI_SCAL_LED`. In `config.h` set how many LED's you want to manually set scaling for. +Eg `#define ISSI_MANUAL_SCALING 3` + +Then Define the array listing all the LEDs you want to override in your `.c`: + +```c +const is31_led PROGMEM g_is31_scaling[ISSI_MANUAL_SCALING] = { +/* LED Index + * | Scaling + * | | */ + {5, 120}, + {9, 120}, + .... +} +``` + +Where LED Index is the position of the LED in the `g_is31_leds` array. The `scaling` value between 0 and 255 to be written to the Scaling Register. + +--- + +## Common Configuration {#common-configuration} + +From this point forward the configuration is the same for all the drivers. The `led_config_t` struct provides a key electrical matrix to led index lookup table, what the physical position of each LED is on the board, and what type of key or usage the LED if the LED represents. Here is a brief example: + +```c +led_config_t g_led_config = { { + // Key Matrix to LED Index + { 5, NO_LED, NO_LED, 0 }, + { NO_LED, NO_LED, NO_LED, NO_LED }, + { 4, NO_LED, NO_LED, 1 }, + { 3, NO_LED, NO_LED, 2 } +}, { + // LED Index to Physical Position + { 188, 16 }, { 187, 48 }, { 149, 64 }, { 112, 64 }, { 37, 48 }, { 38, 16 } +}, { + // LED Index to Flag + 1, 4, 4, 4, 4, 1 +} }; +``` + +The first part, `// Key Matrix to LED Index`, tells the system what key this LED represents by using the key's electrical matrix row & col. The second part, `// LED Index to Physical Position` represents the LED's physical `{ x, y }` position on the keyboard. The default expected range of values for `{ x, y }` is the inclusive range `{ 0..224, 0..64 }`. This default expected range is due to effects that calculate the center of the keyboard for their animations. The easiest way to calculate these positions is imagine your keyboard is a grid, and the top left of the keyboard represents `{ x, y }` coordinate `{ 0, 0 }` and the bottom right of your keyboard represents `{ 224, 64 }`. Using this as a basis, you can use the following formula to calculate the physical position: + +```c +x = 224 / (NUMBER_OF_COLS - 1) * COL_POSITION +y = 64 / (NUMBER_OF_ROWS - 1) * ROW_POSITION +``` + +Where NUMBER_OF_COLS, NUMBER_OF_ROWS, COL_POSITION, & ROW_POSITION are all based on the physical layout of your keyboard, not the electrical layout. + +As mentioned earlier, the center of the keyboard by default is expected to be `{ 112, 32 }`, but this can be changed if you want to more accurately calculate the LED's physical `{ x, y }` positions. Keyboard designers can implement `#define LED_MATRIX_CENTER { 112, 32 }` in their config.h file with the new center point of the keyboard, or where they want it to be allowing more possibilities for the `{ x, y }` values. Do note that the maximum value for x or y is 255, and the recommended maximum is 224 as this gives animations runoff room before they reset. + +`// LED Index to Flag` is a bitmask, whether or not a certain LEDs is of a certain type. It is recommended that LEDs are set to only 1 type. + +## Flags {#flags} + +|Define |Value |Description | +|----------------------------|------|-------------------------------------------------| +|`HAS_FLAGS(bits, flags)` |*n/a* |Evaluates to `true` if `bits` has all `flags` set| +|`HAS_ANY_FLAGS(bits, flags)`|*n/a* |Evaluates to `true` if `bits` has any `flags` set| +|`LED_FLAG_NONE` |`0x00`|If this LED has no flags | +|`LED_FLAG_ALL` |`0xFF`|If this LED has all flags | +|`LED_FLAG_MODIFIER` |`0x01`|If the LED is on a modifier key | +|`LED_FLAG_KEYLIGHT` |`0x04`|If the LED is for key backlight | +|`LED_FLAG_INDICATOR` |`0x08`|If the LED is for keyboard state indication | + +## Keycodes {#keycodes} + +|Key |Aliases |Description | +|-------------------------------|---------|-----------------------------------| +|`QK_LED_MATRIX_ON` |`LM_ON` |Turn on LED Matrix | +|`QK_LED_MATRIX_OFF` |`LM_OFF` |Turn off LED Matrix | +|`QK_LED_MATRIX_TOGGLE` |`LM_TOGG`|Toggle LED Matrix on or off | +|`QK_LED_MATRIX_MODE_NEXT` |`LM_NEXT`|Cycle through animations | +|`QK_LED_MATRIX_MODE_PREVIOUS` |`LM_PREV`|Cycle through animations in reverse| +|`QK_LED_MATRIX_BRIGHTNESS_UP` |`LM_BRIU`|Increase the brightness level | +|`QK_LED_MATRIX_BRIGHTNESS_DOWN`|`LM_BRID`|Decrease the brightness level | +|`QK_LED_MATRIX_SPEED_UP` |`LM_SPDU`|Increase the animation speed | +|`QK_LED_MATRIX_SPEED_DOWN` |`LM_SPDD`|Decrease the animation speed | + +## LED Matrix Effects {#led-matrix-effects} + +These are the effects that are currently available: + +```c +enum led_matrix_effects { + LED_MATRIX_NONE = 0, + LED_MATRIX_SOLID = 1, // Static single val, no speed support + LED_MATRIX_ALPHAS_MODS, // Static dual val, speed is val for LEDs marked as modifiers + LED_MATRIX_BREATHING, // Cycling brightness animation + LED_MATRIX_BAND, // Band fading brightness scrolling left to right + LED_MATRIX_BAND_PINWHEEL, // 3 blade spinning pinwheel fades brightness + LED_MATRIX_BAND_SPIRAL, // Spinning spiral fades brightness + LED_MATRIX_CYCLE_LEFT_RIGHT, // Full gradient scrolling left to right + LED_MATRIX_CYCLE_UP_DOWN, // Full gradient scrolling top to bottom + LED_MATRIX_CYCLE_OUT_IN, // Full gradient scrolling out to in + LED_MATRIX_DUAL_BEACON, // Full gradient spinning around center of keyboard + LED_MATRIX_SOLID_REACTIVE_SIMPLE, // Pulses keys hit then fades out + LED_MATRIX_SOLID_REACTIVE_WIDE, // Value pulses near a single key hit then fades out + LED_MATRIX_SOLID_REACTIVE_MULTIWIDE, // Value pulses near multiple key hits then fades out + LED_MATRIX_SOLID_REACTIVE_CROSS, // Value pulses the same column and row of a single key hit then fades out + LED_MATRIX_SOLID_REACTIVE_MULTICROSS, // Value pulses the same column and row of multiple key hits then fades out + LED_MATRIX_SOLID_REACTIVE_NEXUS, // Value pulses away on the same column and row of a single key hit then fades out + LED_MATRIX_SOLID_REACTIVE_MULTINEXUS, // Value pulses away on the same column and row of multiple key hits then fades out + LED_MATRIX_SOLID_SPLASH, // Value pulses away from a single key hit then fades out + LED_MATRIX_SOLID_MULTISPLASH, // Value pulses away from multiple key hits then fades out + LED_MATRIX_WAVE_LEFT_RIGHT, // Sine wave scrolling from left to right + LED_MATRIX_WAVE_UP_DOWN, // Sine wave scrolling from up to down + LED_MATRIX_EFFECT_MAX +}; +``` + +You can enable a single effect by defining `ENABLE_[EFFECT_NAME]` in your `config.h`: + + +|Define |Description | +|-------------------------------------------------------|----------------------------------------------| +|`#define ENABLE_LED_MATRIX_ALPHAS_MODS` |Enables `LED_MATRIX_ALPHAS_MODS` | +|`#define ENABLE_LED_MATRIX_BREATHING` |Enables `LED_MATRIX_BREATHING` | +|`#define ENABLE_LED_MATRIX_BAND` |Enables `LED_MATRIX_BAND` | +|`#define ENABLE_LED_MATRIX_BAND_PINWHEEL` |Enables `LED_MATRIX_BAND_PINWHEEL` | +|`#define ENABLE_LED_MATRIX_BAND_SPIRAL` |Enables `LED_MATRIX_BAND_SPIRAL` | +|`#define ENABLE_LED_MATRIX_CYCLE_LEFT_RIGHT` |Enables `LED_MATRIX_CYCLE_LEFT_RIGHT` | +|`#define ENABLE_LED_MATRIX_CYCLE_UP_DOWN` |Enables `LED_MATRIX_CYCLE_UP_DOWN` | +|`#define ENABLE_LED_MATRIX_CYCLE_OUT_IN` |Enables `LED_MATRIX_CYCLE_OUT_IN` | +|`#define ENABLE_LED_MATRIX_DUAL_BEACON` |Enables `LED_MATRIX_DUAL_BEACON` | +|`#define ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT` |Enables `LED_MATRIX_WAVE_LEFT_RIGHT` | +|`#define ENABLE_LED_MATRIX_WAVE_UP_DOWN` |Enables `LED_MATRIX_WAVE_UP_DOWN` | + +|Reactive Defines |Description | +|-------------------------------------------------------|----------------------------------------------| +|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE` |Enables `LED_MATRIX_SOLID_REACTIVE_SIMPLE` | +|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE` |Enables `LED_MATRIX_SOLID_REACTIVE_WIDE` | +|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE` |Enables `LED_MATRIX_SOLID_REACTIVE_MULTIWIDE` | +|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS` |Enables `LED_MATRIX_SOLID_REACTIVE_CROSS` | +|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS` |Enables `LED_MATRIX_SOLID_REACTIVE_MULTICROSS`| +|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS` |Enables `LED_MATRIX_SOLID_REACTIVE_NEXUS` | +|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS` |Enables `LED_MATRIX_SOLID_REACTIVE_MULTINEXUS`| +|`#define ENABLE_LED_MATRIX_SOLID_SPLASH` |Enables `LED_MATRIX_SOLID_SPLASH` | +|`#define ENABLE_LED_MATRIX_SOLID_MULTISPLASH` |Enables `LED_MATRIX_SOLID_MULTISPLASH` | + +::: tip +These modes introduce additional logic that can increase firmware size. +::: + +## Custom LED Matrix Effects {#custom-led-matrix-effects} + +By setting `LED_MATRIX_CUSTOM_USER` (and/or `LED_MATRIX_CUSTOM_KB`) in `rules.mk`, new effects can be defined directly from userspace, without having to edit any QMK core files. + +To declare new effects, create a new `led_matrix_user/kb.inc` that looks something like this: + +`led_matrix_user.inc` should go in the root of the keymap directory. +`led_matrix_kb.inc` should go in the root of the keyboard directory. + +To use custom effects in your code, simply prepend `LED_MATRIX_CUSTOM_` to the effect name specified in `LED_MATRIX_EFFECT()`. For example, an effect declared as `LED_MATRIX_EFFECT(my_cool_effect)` would be referenced with: + +```c +led_matrix_mode(led_MATRIX_CUSTOM_my_cool_effect); +``` + +```c +// !!! DO NOT ADD #pragma once !!! // + +// Step 1. +// Declare custom effects using the LED_MATRIX_EFFECT macro +// (note the lack of semicolon after the macro!) +LED_MATRIX_EFFECT(my_cool_effect) +LED_MATRIX_EFFECT(my_cool_effect2) + +// Step 2. +// Define effects inside the `LED_MATRIX_CUSTOM_EFFECT_IMPLS` ifdef block +#ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS + +// e.g: A simple effect, self-contained within a single method +static bool my_cool_effect(effect_params_t* params) { + LED_MATRIX_USE_LIMITS(led_min, led_max); + for (uint8_t i = led_min; i < led_max; i++) { + led_matrix_set_value(i, 0xFF); + } + return led_matrix_check_finished_leds(led_max); +} + +// e.g: A more complex effect, relying on external methods and state, with +// dedicated init and run methods +static uint8_t some_global_state; +static void my_cool_effect2_complex_init(effect_params_t* params) { + some_global_state = 1; +} +static bool my_cool_effect2_complex_run(effect_params_t* params) { + LED_MATRIX_USE_LIMITS(led_min, led_max); + for (uint8_t i = led_min; i < led_max; i++) { + led_matrix_set_value(i, some_global_state++); + } + return led_matrix_check_finished_leds(led_max); +} +static bool my_cool_effect2(effect_params_t* params) { + if (params->init) my_cool_effect2_complex_init(params); + return my_cool_effect2_complex_run(params); +} + +#endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS +``` + +For inspiration and examples, check out the built-in effects under `quantum/led_matrix/animations/`. + + +## Additional `config.h` Options {#additional-configh-options} + +```c +#define LED_MATRIX_KEYRELEASES // reactive effects respond to keyreleases (instead of keypresses) +#define LED_MATRIX_TIMEOUT 0 // number of milliseconds to wait until led automatically turns off +#define LED_MATRIX_SLEEP // turn off effects when suspended +#define LED_MATRIX_LED_PROCESS_LIMIT (LED_MATRIX_LED_COUNT + 4) / 5 // limits the number of LEDs to process in an animation per task run (increases keyboard responsiveness) +#define LED_MATRIX_LED_FLUSH_LIMIT 16 // limits in milliseconds how frequently an animation will update the LEDs. 16 (16ms) is equivalent to limiting to 60fps (increases keyboard responsiveness) +#define LED_MATRIX_MAXIMUM_BRIGHTNESS 255 // limits maximum brightness of LEDs +#define LED_MATRIX_DEFAULT_ON true // Sets the default enabled state, if none has been set +#define LED_MATRIX_DEFAULT_MODE LED_MATRIX_SOLID // Sets the default mode, if none has been set +#define LED_MATRIX_DEFAULT_VAL LED_MATRIX_MAXIMUM_BRIGHTNESS // Sets the default brightness value, if none has been set +#define LED_MATRIX_DEFAULT_SPD 127 // Sets the default animation speed, if none has been set +#define LED_MATRIX_DEFAULT_FLAGS LED_FLAG_ALL // Sets the default LED flags, if none has been set +#define LED_MATRIX_SPLIT { X, Y } // (Optional) For split keyboards, the number of LEDs connected on each half. X = left, Y = Right. + // If reactive effects are enabled, you also will want to enable SPLIT_TRANSPORT_MIRROR +``` + +## EEPROM storage {#eeprom-storage} + +The EEPROM for it is currently shared with the RGB Matrix system (it's generally assumed only one feature would be used at a time). + +### Direct Operation {#direct-operation} +|Function |Description | +|--------------------------------------------|-------------| +|`led_matrix_set_value_all(v)` |Set all of the LEDs to the given value, where `v` is between 0 and 255 (not written to EEPROM) | +|`led_matrix_set_value(index, v)` |Set a single LED to the given value, where `v` is between 0 and 255, and `index` is between 0 and `LED_MATRIX_LED_COUNT` (not written to EEPROM) | + +### Disable/Enable Effects {#disable-enable-effects} +|Function |Description | +|--------------------------------------------|-------------| +|`led_matrix_toggle()` |Toggle effect range LEDs between on and off | +|`led_matrix_toggle_noeeprom()` |Toggle effect range LEDs between on and off (not written to EEPROM) | +|`led_matrix_enable()` |Turn effect range LEDs on, based on their previous state | +|`led_matrix_enable_noeeprom()` |Turn effect range LEDs on, based on their previous state (not written to EEPROM) | +|`led_matrix_disable()` |Turn effect range LEDs off, based on their previous state | +|`led_matrix_disable_noeeprom()` |Turn effect range LEDs off, based on their previous state (not written to EEPROM) | + +### Change Effect Mode {#change-effect-mode} +|Function |Description | +|--------------------------------------------|-------------| +|`led_matrix_mode(mode)` |Set the mode, if LED animations are enabled | +|`led_matrix_mode_noeeprom(mode)` |Set the mode, if LED animations are enabled (not written to EEPROM) | +|`led_matrix_step()` |Change the mode to the next LED animation in the list of enabled LED animations | +|`led_matrix_step_noeeprom()` |Change the mode to the next LED animation in the list of enabled LED animations (not written to EEPROM) | +|`led_matrix_step_reverse()` |Change the mode to the previous LED animation in the list of enabled LED animations | +|`led_matrix_step_reverse_noeeprom()` |Change the mode to the previous LED animation in the list of enabled LED animations (not written to EEPROM) | +|`led_matrix_increase_speed()` |Increase the speed of the animations | +|`led_matrix_increase_speed_noeeprom()` |Increase the speed of the animations (not written to EEPROM) | +|`led_matrix_decrease_speed()` |Decrease the speed of the animations | +|`led_matrix_decrease_speed_noeeprom()` |Decrease the speed of the animations (not written to EEPROM) | +|`led_matrix_set_speed(speed)` |Set the speed of the animations to the given value where `speed` is between 0 and 255 | +|`led_matrix_set_speed_noeeprom(speed)` |Set the speed of the animations to the given value where `speed` is between 0 and 255 (not written to EEPROM) | + +### Change Value {#change-value} +|Function |Description | +|--------------------------------------------|-------------| +|`led_matrix_increase_val()` |Increase the value for effect range LEDs. This wraps around at maximum value | +|`led_matrix_increase_val_noeeprom()` |Increase the value for effect range LEDs. This wraps around at maximum value (not written to EEPROM) | +|`led_matrix_decrease_val()` |Decrease the value for effect range LEDs. This wraps around at minimum value | +|`led_matrix_decrease_val_noeeprom()` |Decrease the value for effect range LEDs. This wraps around at minimum value (not written to EEPROM) | + +### Query Current Status {#query-current-status} +|Function |Description | +|---------------------------------|---------------------------| +|`led_matrix_is_enabled()` |Gets current on/off status | +|`led_matrix_get_mode()` |Gets current mode | +|`led_matrix_get_val()` |Gets current val | +|`led_matrix_get_speed()` |Gets current speed | +|`led_matrix_get_suspend_state()` |Gets current suspend state | + +## Callbacks {#callbacks} + +### Indicators {#indicators} + +If you want to set custom indicators, such as an LED for Caps Lock, or layer indication, then you can use the `led_matrix_indicators_kb` function on the keyboard level source file, or `led_matrix_indicators_user` function in the user `keymap.c`. +```c +bool led_matrix_indicators_kb(void) { + if (!led_matrix_indicators_user()) { + return false; + } + led_matrix_set_value(index, value); + return true; +} +``` + +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. This includes a special macro to help make this easier to use: `LED_MATRIX_INDICATOR_SET_VALUE(i, v)`. + +```c +void led_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) { + LED_MATRIX_INDICATOR_SET_VALUE(index, value); + return false; +} +``` diff --git a/docs/features/midi.md b/docs/features/midi.md new file mode 100644 index 0000000000..32c062e54d --- /dev/null +++ b/docs/features/midi.md @@ -0,0 +1,264 @@ +# MIDI + +## Usage + +First, enable MIDI by adding the following to your `rules.mk`: + +```make +MIDI_ENABLE = yes +``` + +There are two MIDI systems in QMK: basic and advanced. With basic MIDI you will only be able to send Note On and Note Off messages using the note keycodes, meaning that keycodes like `MI_OCTU` and `MI_OCTD` will not work. Advanced MIDI allows you to do things like octave shifts, channel changes, velocity changes, modulation, and more. + +### Caveats + +MIDI requires 2 USB endpoints and as such may not work on some hardware such as V-USB controllers. + +### Basic MIDI + +To enable basic MIDI, add the following to your `config.h`: + +```c +#define MIDI_BASIC +``` + +### Advanced MIDI + +To enable advanced MIDI, add the following to your `config.h`: + +```c +#define MIDI_ADVANCED +``` + +#### Sending Control Change (CC) Messages + +If you're aiming to emulate the features of something like a Launchpad or other MIDI controller you'll need to access the internal MIDI device directly. + +Because there are so many possible CC messages, not all of them are implemented as keycodes. Additionally, you might need to provide more than just two values that you would get from a keycode (pressed and released) - for example, the analog values from a fader or a potentiometer. So, you will need to implement [custom keycodes](../feature_macros) if you want to use them in your keymap directly using `process_record_user()`. + + +For reference of all the possible control code numbers see [MIDI Specification](#midi-specification) + +#### Example code for using Generic On Off Switches as per MIDI Specification. +```c +#include QMK_KEYBOARD_H + +extern MidiDevice midi_device; + +// MIDI CC codes for generic on/off switches (80, 81, 82, 83) +// Off: 0-63 +// On: 64-127 + +#define MIDI_CC_OFF 0 +#define MIDI_CC_ON 127 + +enum custom_keycodes { + MIDI_CC80 = SAFE_RANGE, +}; + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case MIDI_CC80: + if (record->event.pressed) { + midi_send_cc(&midi_device, midi_config.channel, 80, MIDI_CC_ON); + } else { + midi_send_cc(&midi_device, midi_config.channel, 80, MIDI_CC_OFF); + } + return true; + } + return true; +}; + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + LAYOUT( + // ... + MIDI_CC80, + // ... + ) +}; +``` + +### Keycodes + +|Keycode |Aliases |Description | +|-------------------------------|------------------|---------------------------------| +|`QK_MIDI_ON` |`MI_ON` |Turn MIDI on | +|`QK_MIDI_OFF` |`MI_OFF` |Turn MIDI off | +|`QK_MIDI_TOGGLE` |`MI_TOGG` |Toggle MIDI enabled | +|`QK_MIDI_NOTE_C_0` |`MI_C` |C octave 0 | +|`QK_MIDI_NOTE_C_SHARP_0` |`MI_Cs`, `MI_Db` |C♯/D♭ octave 0 | +|`QK_MIDI_NOTE_D_0` |`MI_D` |D octave 0 | +|`QK_MIDI_NOTE_D_SHARP_0` |`MI_Ds`, `MI_Eb` |D♯/E♭ octave 0 | +|`QK_MIDI_NOTE_E_0` |`MI_E` |E octave 0 | +|`QK_MIDI_NOTE_F_0` |`MI_F` |F octave 0 | +|`QK_MIDI_NOTE_F_SHARP_0` |`MI_Fs`, `MI_Gb` |F♯/G♭ octave 0 | +|`QK_MIDI_NOTE_G_0` |`MI_G` |G octave 0 | +|`QK_MIDI_NOTE_G_SHARP_0` |`MI_Gs`, `MI_Ab` |G♯/A♭ octave 0 | +|`QK_MIDI_NOTE_A_0` |`MI_A` |A octave 0 | +|`QK_MIDI_NOTE_A_SHARP_0` |`MI_As`, `MI_Bb` |A♯/B♭ octave 0 | +|`QK_MIDI_NOTE_B_0` |`MI_B` |B octave 0 | +|`QK_MIDI_NOTE_C_1` |`MI_C1` |C octave 1 | +|`QK_MIDI_NOTE_C_SHARP_1` |`MI_Cs1`, `MI_Db1`|C♯/D♭ octave 1 | +|`QK_MIDI_NOTE_D_1` |`MI_D1` |D octave 1 | +|`QK_MIDI_NOTE_D_SHARP_1` |`MI_Ds1`, `MI_Eb1`|D♯/E♭ octave 1 | +|`QK_MIDI_NOTE_E_1` |`MI_E1` |E octave 1 | +|`QK_MIDI_NOTE_F_1` |`MI_F1` |F octave 1 | +|`QK_MIDI_NOTE_F_SHARP_1` |`MI_Fs1`, `MI_Gb1`|F♯/G♭ octave 1 | +|`QK_MIDI_NOTE_G_1` |`MI_G1` |G octave 1 | +|`QK_MIDI_NOTE_G_SHARP_1` |`MI_Gs1`, `MI_Ab1`|G♯/A♭ octave 1 | +|`QK_MIDI_NOTE_A_1` |`MI_A1` |A octave 1 | +|`QK_MIDI_NOTE_A_SHARP_1` |`MI_As1`, `MI_Bb1`|A♯/B♭ octave 1 | +|`QK_MIDI_NOTE_B_1` |`MI_B1` |B octave 1 | +|`QK_MIDI_NOTE_C_2` |`MI_C2` |C octave 2 | +|`QK_MIDI_NOTE_C_SHARP_2` |`MI_Cs2`, `MI_Db2`|C♯/D♭ octave 2 | +|`QK_MIDI_NOTE_D_2` |`MI_D2` |D octave 2 | +|`QK_MIDI_NOTE_D_SHARP_2` |`MI_Ds2`, `MI_Eb2`|D♯/E♭ octave 2 | +|`QK_MIDI_NOTE_E_2` |`MI_E2` |E octave 2 | +|`QK_MIDI_NOTE_F_2` |`MI_F2` |F octave 2 | +|`QK_MIDI_NOTE_F_SHARP_2` |`MI_Fs2`, `MI_Gb2`|F♯/G♭ octave 2 | +|`QK_MIDI_NOTE_G_2` |`MI_G2` |G octave 2 | +|`QK_MIDI_NOTE_G_SHARP_2` |`MI_Gs2`, `MI_Ab2`|G♯/A♭ octave 2 | +|`QK_MIDI_NOTE_A_2` |`MI_A2` |A octave 2 | +|`QK_MIDI_NOTE_A_SHARP_2` |`MI_As2`, `MI_Bb2`|A♯/B♭ octave 2 | +|`QK_MIDI_NOTE_B_2` |`MI_B2` |B octave 2 | +|`QK_MIDI_NOTE_C_3` |`MI_C3` |C octave 3 | +|`QK_MIDI_NOTE_C_SHARP_3` |`MI_Cs3`, `MI_Db3`|C♯/D♭ octave 3 | +|`QK_MIDI_NOTE_D_3` |`MI_D3` |D octave 3 | +|`QK_MIDI_NOTE_D_SHARP_3` |`MI_Ds3`, `MI_Eb3`|D♯/E♭ octave 3 | +|`QK_MIDI_NOTE_E_3` |`MI_E3` |E octave 3 | +|`QK_MIDI_NOTE_F_3` |`MI_F3` |F octave 3 | +|`QK_MIDI_NOTE_F_SHARP_3` |`MI_Fs3`, `MI_Gb3`|F♯/G♭ octave 3 | +|`QK_MIDI_NOTE_G_3` |`MI_G3` |G octave 3 | +|`QK_MIDI_NOTE_G_SHARP_3` |`MI_Gs3`, `MI_Ab3`|G♯/A♭ octave 3 | +|`QK_MIDI_NOTE_A_3` |`MI_A3` |A octave 3 | +|`QK_MIDI_NOTE_A_SHARP_3` |`MI_As3`, `MI_Bb3`|A♯/B♭ octave 3 | +|`QK_MIDI_NOTE_B_3` |`MI_B3` |B octave 3 | +|`QK_MIDI_NOTE_C_4` |`MI_C4` |C octave 4 | +|`QK_MIDI_NOTE_C_SHARP_4` |`MI_Cs4`, `MI_Db4`|C♯/D♭ octave 4 | +|`QK_MIDI_NOTE_D_4` |`MI_D4` |D octave 4 | +|`QK_MIDI_NOTE_D_SHARP_4` |`MI_Ds4`, `MI_Eb4`|D♯/E♭ octave 4 | +|`QK_MIDI_NOTE_E_4` |`MI_E4` |E octave 4 | +|`QK_MIDI_NOTE_F_4` |`MI_F4` |F octave 4 | +|`QK_MIDI_NOTE_F_SHARP_4` |`MI_Fs4`, `MI_Gb4`|F♯/G♭ octave 4 | +|`QK_MIDI_NOTE_G_4` |`MI_G4` |G octave 4 | +|`QK_MIDI_NOTE_G_SHARP_4` |`MI_Gs4`, `MI_Ab4`|G♯/A♭ octave 4 | +|`QK_MIDI_NOTE_A_4` |`MI_A4` |A octave 4 | +|`QK_MIDI_NOTE_A_SHARP_4` |`MI_As4`, `MI_Bb4`|A♯/B♭ octave 4 | +|`QK_MIDI_NOTE_B_4` |`MI_B4` |B octave 4 | +|`QK_MIDI_NOTE_C_5` |`MI_C5` |C octave 5 | +|`QK_MIDI_NOTE_C_SHARP_5` |`MI_Cs5`, `MI_Db5`|C♯/D♭ octave 5 | +|`QK_MIDI_NOTE_D_5` |`MI_D5` |D octave 5 | +|`QK_MIDI_NOTE_D_SHARP_5` |`MI_Ds5`, `MI_Eb5`|D♯/E♭ octave 5 | +|`QK_MIDI_NOTE_E_5` |`MI_E5` |E octave 5 | +|`QK_MIDI_NOTE_F_5` |`MI_F5` |F octave 5 | +|`QK_MIDI_NOTE_F_SHARP_5` |`MI_Fs5`, `MI_Gb5`|F♯/G♭ octave 5 | +|`QK_MIDI_NOTE_G_5` |`MI_G5` |G octave 5 | +|`QK_MIDI_NOTE_G_SHARP_5` |`MI_Gs5`, `MI_Ab5`|G♯/A♭ octave 5 | +|`QK_MIDI_NOTE_A_5` |`MI_A5` |A octave 5 | +|`QK_MIDI_NOTE_A_SHARP_5` |`MI_As5`, `MI_Bb5`|A♯/B♭ octave 5 | +|`QK_MIDI_NOTE_B_5` |`MI_B5` |B octave 5 | +|`QK_MIDI_OCTAVE_N2` |`MI_OCN2` |Set octave to -2 | +|`QK_MIDI_OCTAVE_N1` |`MI_OCN1` |Set octave to -1 | +|`QK_MIDI_OCTAVE_0` |`MI_OC0` |Set octave to 0 | +|`QK_MIDI_OCTAVE_1` |`MI_OC1` |Set octave to 1 | +|`QK_MIDI_OCTAVE_2` |`MI_OC2` |Set octave to 2 | +|`QK_MIDI_OCTAVE_3` |`MI_OC3` |Set octave to 3 | +|`QK_MIDI_OCTAVE_4` |`MI_OC4` |Set octave to 4 | +|`QK_MIDI_OCTAVE_5` |`MI_OC5` |Set octave to 5 | +|`QK_MIDI_OCTAVE_6` |`MI_OC6` |Set octave to 6 | +|`QK_MIDI_OCTAVE_7` |`MI_OC7` |Set octave to 7 | +|`QK_MIDI_OCTAVE_DOWN` |`MI_OCTD` |Move down an octave | +|`QK_MIDI_OCTAVE_UP` |`MI_OCTU` |Move up an octave | +|`QK_MIDI_TRANSPOSE_N6` |`MI_TRN6` |Set transposition to -6 semitones| +|`QK_MIDI_TRANSPOSE_N5` |`MI_TRN5` |Set transposition to -5 semitones| +|`QK_MIDI_TRANSPOSE_N4` |`MI_TRN4` |Set transposition to -4 semitones| +|`QK_MIDI_TRANSPOSE_N3` |`MI_TRN3` |Set transposition to -3 semitones| +|`QK_MIDI_TRANSPOSE_N2` |`MI_TRN2` |Set transposition to -2 semitones| +|`QK_MIDI_TRANSPOSE_N1` |`MI_TRN1` |Set transposition to -1 semitone | +|`QK_MIDI_TRANSPOSE_0` |`MI_TR0` |No transposition | +|`QK_MIDI_TRANSPOSE_1` |`MI_TR1` |Set transposition to +1 semitone | +|`QK_MIDI_TRANSPOSE_2` |`MI_TR2` |Set transposition to +2 semitones| +|`QK_MIDI_TRANSPOSE_3` |`MI_TR3` |Set transposition to +3 semitones| +|`QK_MIDI_TRANSPOSE_4` |`MI_TR4` |Set transposition to +4 semitones| +|`QK_MIDI_TRANSPOSE_5` |`MI_TR5` |Set transposition to +5 semitones| +|`QK_MIDI_TRANSPOSE_6` |`MI_TR6` |Set transposition to +6 semitones| +|`QK_MIDI_TRANSPOSE_DOWN` |`MI_TRSD` |Decrease transposition | +|`QK_MIDI_TRANSPOSE_UP` |`MI_TRSU` |Increase transposition | +|`QK_MIDI_VELOCITY_0` |`MI_VL0` |Set velocity to 0 | +|`QK_MIDI_VELOCITY_1` |`MI_VL1` |Set velocity to 12 | +|`QK_MIDI_VELOCITY_2` |`MI_VL2` |Set velocity to 25 | +|`QK_MIDI_VELOCITY_3` |`MI_VL3` |Set velocity to 38 | +|`QK_MIDI_VELOCITY_4` |`MI_VL4` |Set velocity to 51 | +|`QK_MIDI_VELOCITY_5` |`MI_VL5` |Set velocity to 64 | +|`QK_MIDI_VELOCITY_6` |`MI_VL6` |Set velocity to 76 | +|`QK_MIDI_VELOCITY_7` |`MI_VL7` |Set velocity to 89 | +|`QK_MIDI_VELOCITY_8` |`MI_VL8` |Set velocity to 102 | +|`QK_MIDI_VELOCITY_9` |`MI_VL9` |Set velocity to 114 | +|`QK_MIDI_VELOCITY_10` |`MI_VL10` |Set velocity to 127 | +|`QK_MIDI_VELOCITY_DOWN` |`MI_VELD` |Decrease velocity | +|`QK_MIDI_VELOCITY_UP` |`MI_VELU` |Increase velocity | +|`QK_MIDI_CHANNEL_1` |`MI_CH1` |Set channel to 1 | +|`QK_MIDI_CHANNEL_2` |`MI_CH2` |Set channel to 2 | +|`QK_MIDI_CHANNEL_3` |`MI_CH3` |Set channel to 3 | +|`QK_MIDI_CHANNEL_4` |`MI_CH4` |Set channel to 4 | +|`QK_MIDI_CHANNEL_5` |`MI_CH5` |Set channel to 5 | +|`QK_MIDI_CHANNEL_6` |`MI_CH6` |Set channel to 6 | +|`QK_MIDI_CHANNEL_7` |`MI_CH7` |Set channel to 7 | +|`QK_MIDI_CHANNEL_8` |`MI_CH8` |Set channel to 8 | +|`QK_MIDI_CHANNEL_9` |`MI_CH9` |Set channel to 9 | +|`QK_MIDI_CHANNEL_10` |`MI_CH10` |Set channel to 10 | +|`QK_MIDI_CHANNEL_11` |`MI_CH11` |Set channel to 11 | +|`QK_MIDI_CHANNEL_12` |`MI_CH12` |Set channel to 12 | +|`QK_MIDI_CHANNEL_13` |`MI_CH13` |Set channel to 13 | +|`QK_MIDI_CHANNEL_14` |`MI_CH14` |Set channel to 14 | +|`QK_MIDI_CHANNEL_15` |`MI_CH15` |Set channel to 15 | +|`QK_MIDI_CHANNEL_16` |`MI_CH16` |Set channel to 16 | +|`QK_MIDI_CHANNEL_DOWN` |`MI_CHND` |Decrease channel | +|`QK_MIDI_CHANNEL_UP` |`MI_CHNU` |Increase channel | +|`QK_MIDI_ALL_NOTES_OFF` |`MI_AOFF` |Stop all notes | +|`QK_MIDI_SUSTAIN` |`MI_SUST` |Sustain | +|`QK_MIDI_PORTAMENTO` |`MI_PORT` |Portmento | +|`QK_MIDI_SOSTENUTO` |`MI_SOST` |Sostenuto | +|`QK_MIDI_SOFT` |`MI_SOFT` |Soft Pedal | +|`QK_MIDI_LEGATO` |`MI_LEG` |Legato | +|`QK_MIDI_MODULATION` |`MI_MOD` |Modulation | +|`QK_MIDI_MODULATION_SPEED_DOWN`|`MI_MODD` |Decrease modulation speed | +|`QK_MIDI_MODULATION_SPEED_UP` |`MI_MODU` |Increase modulation speed | +|`QK_MIDI_PITCH_BEND_DOWN` |`MI_BNDD` |Bend pitch down | +|`QK_MIDI_PITCH_BEND_UP` |`MI_BNDU` |Bend pitch up | + +### Configuration + +Certain values are stored in the `midi_config` struct. This configuration is not persisted to EEPROM. By default, these values are: + +|Configuration |Value|Comments | +|-------------------|-----|-----------------------| +|Octave |`4` |Corresponds to `MI_OC2`| +|Transposition |`0` | | +|Velocity |`127`| | +|Channel |`0` | | +|Modulation Interval|`8` | | + +For the above, the `MI_C` keycode will produce a C3 (note number 48), and so on. + +### References +#### MIDI Specification + + * [MIDI.org](https://www.midi.org/specifications-old/item/table-1-summary-of-midi-message) + * [CMU MIDI Programmer's Reference](https://www.cs.cmu.edu/~music/cmsip/readings/MIDI%20tutorial%20for%20programmers.html) +#### QMK C Files + + * `quantum/process_keycode/process_midi.c` + * `quantum/quantum_keycodes.h` + * `quantum/midi/midi.h` + * `quantum/midi/midi.c` + * `quantum/midi/qmk_midi.c` + * `quantum/midi/midi_device.h` + + diff --git a/docs/features/mouse_keys.md b/docs/features/mouse_keys.md new file mode 100644 index 0000000000..c2b3e98f42 --- /dev/null +++ b/docs/features/mouse_keys.md @@ -0,0 +1,208 @@ +# Mouse keys + +Mouse keys is a feature that allows you to emulate a mouse using your keyboard. You can move the pointer at different speeds, press 5 buttons and scroll in 8 directions. + +## Adding mouse keys to your keyboard + +To use mouse keys, you must at least enable mouse keys support and map mouse actions to keys on your keyboard. + +### Enabling mouse keys + +To enable mouse keys, add the following line to your keymap’s `rules.mk`: + +```c +MOUSEKEY_ENABLE = yes +``` + +### Mapping mouse actions + +In your keymap you can use the following keycodes to map key presses to mouse actions: + +|Key |Aliases |Description | +|----------------|---------|-----------------| +|`KC_MS_UP` |`KC_MS_U`|Move cursor up | +|`KC_MS_DOWN` |`KC_MS_D`|Move cursor down | +|`KC_MS_LEFT` |`KC_MS_L`|Move cursor left | +|`KC_MS_RIGHT` |`KC_MS_R`|Move cursor right| +|`KC_MS_BTN1` |`KC_BTN1`|Press button 1 | +|`KC_MS_BTN2` |`KC_BTN2`|Press button 2 | +|`KC_MS_BTN3` |`KC_BTN3`|Press button 3 | +|`KC_MS_BTN4` |`KC_BTN4`|Press button 4 | +|`KC_MS_BTN5` |`KC_BTN5`|Press button 5 | +|`KC_MS_BTN6` |`KC_BTN6`|Press button 6 | +|`KC_MS_BTN7` |`KC_BTN7`|Press button 7 | +|`KC_MS_BTN8` |`KC_BTN8`|Press button 8 | +|`KC_MS_WH_UP` |`KC_WH_U`|Move wheel up | +|`KC_MS_WH_DOWN` |`KC_WH_D`|Move wheel down | +|`KC_MS_WH_LEFT` |`KC_WH_L`|Move wheel left | +|`KC_MS_WH_RIGHT`|`KC_WH_R`|Move wheel right | +|`KC_MS_ACCEL0` |`KC_ACL0`|Set speed to 0 | +|`KC_MS_ACCEL1` |`KC_ACL1`|Set speed to 1 | +|`KC_MS_ACCEL2` |`KC_ACL2`|Set speed to 2 | + +## Configuring mouse keys + +Mouse keys supports three different modes to move the cursor: + +* **Accelerated (default):** Holding movement keys accelerates the cursor until it reaches its maximum speed. +* **Kinetic:** Holding movement keys accelerates the cursor with its speed following a quadratic curve until it reaches its maximum speed. +* **Constant:** Holding movement keys moves the cursor at constant speeds. +* **Combined:** Holding movement keys accelerates the cursor until it reaches its maximum speed, but holding acceleration and movement keys simultaneously moves the cursor at constant speeds. +* **Inertia:** Cursor accelerates when key held, and decelerates after key release. Tracks X and Y velocity separately for more nuanced movements. Applies to cursor only, not scrolling. + +The same principle applies to scrolling, in most modes. + +Configuration options that are times, intervals or delays are given in milliseconds. Scroll speed is given as multiples of the default scroll step. For example, a scroll speed of 8 means that each scroll action covers 8 times the length of the default scroll step as defined by your operating system or application. + +### Accelerated mode + +This is the default mode. You can adjust the cursor and scrolling acceleration using the following settings in your keymap’s `config.h` file: + +|Define |Default|Description | +|----------------------------|-------|---------------------------------------------------------| +|`MOUSEKEY_DELAY` |10 |Delay between pressing a movement key and cursor movement| +|`MOUSEKEY_INTERVAL` |20 |Time between cursor movements in milliseconds | +|`MOUSEKEY_MOVE_DELTA` |8 |Step size | +|`MOUSEKEY_MAX_SPEED` |10 |Maximum cursor speed at which acceleration stops | +|`MOUSEKEY_TIME_TO_MAX` |30 |Time until maximum cursor speed is reached | +|`MOUSEKEY_WHEEL_DELAY` |10 |Delay between pressing a wheel key and wheel movement | +|`MOUSEKEY_WHEEL_INTERVAL` |80 |Time between wheel movements | +|`MOUSEKEY_WHEEL_DELTA` |1 |Wheel movement step size | +|`MOUSEKEY_WHEEL_MAX_SPEED` |8 |Maximum number of scroll steps per scroll action | +|`MOUSEKEY_WHEEL_TIME_TO_MAX`|40 |Time until maximum scroll speed is reached | + +Tips: + +* Setting `MOUSEKEY_DELAY` too low makes the cursor unresponsive. Setting it too high makes small movements difficult. +* For smoother cursor movements, lower the value of `MOUSEKEY_INTERVAL`. If the refresh rate of your display is 60Hz, you could set it to `16` (1/60). As this raises the cursor speed significantly, you may want to lower `MOUSEKEY_MAX_SPEED`. +* Setting `MOUSEKEY_TIME_TO_MAX` or `MOUSEKEY_WHEEL_TIME_TO_MAX` to `0` will disable acceleration for the cursor or scrolling respectively. This way you can make one of them constant while keeping the other accelerated, which is not possible in constant speed mode. +* Setting `MOUSEKEY_WHEEL_INTERVAL` too low will make scrolling too fast. Setting it too high will make scrolling too slow when the wheel key is held down. + +Cursor acceleration uses the same algorithm as the X Window System MouseKeysAccel feature. You can read more about it [on Wikipedia](https://en.wikipedia.org/wiki/Mouse_keys). + +### Kinetic Mode + +This is an extension of the accelerated mode. The kinetic mode uses a quadratic curve on the cursor speed which allows precise movements at the beginning and allows to cover large distances by increasing cursor speed quickly thereafter. You can adjust the cursor and scrolling acceleration using the following settings in your keymap’s `config.h` file: + +|Define |Default |Description | +|--------------------------------------|---------|---------------------------------------------------------------| +|`MK_KINETIC_SPEED` |undefined|Enable kinetic mode | +|`MOUSEKEY_DELAY` |5 |Delay between pressing a movement key and cursor movement | +|`MOUSEKEY_INTERVAL` |10 |Time between cursor movements in milliseconds | +|`MOUSEKEY_MOVE_DELTA` |16 |Step size for accelerating from initial to base speed | +|`MOUSEKEY_INITIAL_SPEED` |100 |Initial speed of the cursor in pixel per second | +|`MOUSEKEY_BASE_SPEED` |5000 |Maximum cursor speed at which acceleration stops | +|`MOUSEKEY_DECELERATED_SPEED` |400 |Decelerated cursor speed | +|`MOUSEKEY_ACCELERATED_SPEED` |3000 |Accelerated cursor speed | +|`MOUSEKEY_WHEEL_INITIAL_MOVEMENTS` |16 |Initial number of movements of the mouse wheel | +|`MOUSEKEY_WHEEL_BASE_MOVEMENTS` |32 |Maximum number of movements at which acceleration stops | +|`MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS`|48 |Accelerated wheel movements | +|`MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS`|8 |Decelerated wheel movements | + +Tips: + +* The smoothness of the cursor movement depends on the `MOUSEKEY_INTERVAL` setting. The shorter the interval is set the smoother the movement will be. Setting the value too low makes the cursor unresponsive. Lower settings are possible if the micro processor is fast enough. For example: At an interval of `8` milliseconds, `125` movements per second will be initiated. With a base speed of `1000` each movement will move the cursor by `8` pixels. +* Mouse wheel movements are implemented differently from cursor movements. While it's okay for the cursor to move multiple pixels at once for the mouse wheel this would lead to jerky movements. Instead, the mouse wheel operates at step size `1`. Setting mouse wheel speed is done by adjusting the number of wheel movements per second. + +### Constant mode + +In this mode you can define multiple different speeds for both the cursor and the mouse wheel. There is no acceleration. `KC_ACL0`, `KC_ACL1` and `KC_ACL2` change the cursor and scroll speed to their respective setting. + +You can choose whether speed selection is momentary or tap-to-select: + +* **Momentary:** The chosen speed is only active while you hold the respective key. When the key is raised, mouse keys returns to the unmodified speed. +* **Tap-to-select:** The chosen speed is activated when you press the respective key and remains active even after the key has been raised. The default speed is that of `KC_ACL1`. There is no unmodified speed. + +The default speeds from slowest to fastest are as follows: + +* **Momentary:** `KC_ACL0` < `KC_ACL1` < *unmodified* < `KC_ACL2` +* **Tap-to-select:** `KC_ACL0` < `KC_ACL1` < `KC_ACL2` + +To use constant speed mode, you must at least define `MK_3_SPEED` in your keymap’s `config.h` file: + +```c +#define MK_3_SPEED +``` + +To enable momentary mode, also define `MK_MOMENTARY_ACCEL`: + +```c +#define MK_MOMENTARY_ACCEL +``` + +Use the following settings if you want to adjust cursor movement or scrolling: + +|Define |Default |Description | +|---------------------|-------------|-------------------------------------------| +|`MK_3_SPEED` |*Not defined*|Enable constant cursor speeds | +|`MK_MOMENTARY_ACCEL` |*Not defined*|Enable momentary speed selection | +|`MK_C_OFFSET_UNMOD` |16 |Cursor offset per movement (unmodified) | +|`MK_C_INTERVAL_UNMOD`|16 |Time between cursor movements (unmodified) | +|`MK_C_OFFSET_0` |1 |Cursor offset per movement (`KC_ACL0`) | +|`MK_C_INTERVAL_0` |32 |Time between cursor movements (`KC_ACL0`) | +|`MK_C_OFFSET_1` |4 |Cursor offset per movement (`KC_ACL1`) | +|`MK_C_INTERVAL_1` |16 |Time between cursor movements (`KC_ACL1`) | +|`MK_C_OFFSET_2` |32 |Cursor offset per movement (`KC_ACL2`) | +|`MK_C_INTERVAL_2` |16 |Time between cursor movements (`KC_ACL2`) | +|`MK_W_OFFSET_UNMOD` |1 |Scroll steps per scroll action (unmodified)| +|`MK_W_INTERVAL_UNMOD`|40 |Time between scroll steps (unmodified) | +|`MK_W_OFFSET_0` |1 |Scroll steps per scroll action (`KC_ACL0`) | +|`MK_W_INTERVAL_0` |360 |Time between scroll steps (`KC_ACL0`) | +|`MK_W_OFFSET_1` |1 |Scroll steps per scroll action (`KC_ACL1`) | +|`MK_W_INTERVAL_1` |120 |Time between scroll steps (`KC_ACL1`) | +|`MK_W_OFFSET_2` |1 |Scroll steps per scroll action (`KC_ACL2`) | +|`MK_W_INTERVAL_2` |20 |Time between scroll steps (`KC_ACL2`) | + +### Combined mode + +This mode functions like **Accelerated** mode, however, you can hold `KC_ACL0`, `KC_ACL1` and `KC_ACL2` +to momentarily (while held) set the cursor and scroll speeds to constant speeds. When no acceleration +keys are held, this mode is identical to **Accelerated** mode, and can be modified using all of the +relevant settings. + +* **KC_ACL0:** This acceleration sets your cursor to the slowest possible speed. This is useful for very +small and detailed movements of the cursor. +* **KC_ACL1:** This acceleration sets your cursor to half the maximum (user defined) speed. +* **KC_ACL2:** This acceleration sets your cursor to the maximum (computer defined) speed. This is +useful for moving the cursor large distances without much accuracy. + +To use combined speed mode, you must at least define `MK_COMBINED` in your keymap’s `config.h` file: + +```c +#define MK_COMBINED +``` + +### Inertia mode + +This mode provides smooth motion, like sliding on ice. The cursor accelerates +along a quadratic curve while a key is held, then glides to a stop after the +key is released. Vertical and horizontal movements are tracked independently, +so the cursor can move in many directions and make curves. + +Cannot be used at the same time as Kinetic mode, Constant mode, or Combined mode. + +Recommended settings in your keymap’s `config.h` file: + +|Define |Default |Description | +|----------------------------|---------|-----------------------------------------------------------| +|`MOUSEKEY_INERTIA` |undefined|Enable Inertia mode | +|`MOUSEKEY_DELAY` |150 |Delay between pressing a movement key and cursor movement | +|`MOUSEKEY_INTERVAL` |16 |Time between cursor movements in milliseconds (16 = 60fps) | +|`MOUSEKEY_MAX_SPEED` |32 |Maximum cursor speed at which acceleration stops | +|`MOUSEKEY_TIME_TO_MAX` |32 |Number of frames until maximum cursor speed is reached | +|`MOUSEKEY_FRICTION` |24 |How quickly the cursor stops after releasing a key | +|`MOUSEKEY_MOVE_DELTA` |1 |How much to move on first frame (1 strongly recommended) | + +Tips: + +* Set `MOUSEKEY_DELAY` to roughly the same value as your host computer's key repeat delay, in ms. Recommended values are 100 to 300. +* Set `MOUSEKEY_INTERVAL` to a value of 1000 / your monitor's FPS. For 60 FPS, 1000/60 = 16. +* Set `MOUSEKEY_MAX_SPEED` based on your screen resolution and refresh rate, like Width / FPS. For example, 1920 pixels / 60 FPS = 32 pixels per frame. +* Set `MOUSEKEY_TIME_TO_MAX` to a value of approximately FPS / 2, to make it reach full speed in half a second (or so). +* Set `MOUSEKEY_FRICTION` to something between 1 and 255. Lower makes the cursor glide longer. Values from 8 to 40 are the most effective. +* Keep `MOUSEKEY_MOVE_DELTA` at 1. This allows precise movements before the gliding effect starts. +* Mouse wheel options are the same as the default accelerated mode, and do not use inertia. + +## Use with PS/2 Mouse and Pointing Device + +Mouse keys button state is shared with [PS/2 mouse](ps2_mouse) and [pointing device](pointing_device) so mouse keys button presses can be used for clicks and drags. diff --git a/docs/features/oled_driver.md b/docs/features/oled_driver.md new file mode 100644 index 0000000000..cb7a2f13f3 --- /dev/null +++ b/docs/features/oled_driver.md @@ -0,0 +1,499 @@ +# OLED Driver + +## Supported Hardware + +OLED modules using SSD1306, SH1106 or SH1107 driver ICs, communicating over I2C or SPI. +Tested combinations: + +|IC |Size |Platform|Notes | +|---------|-------|--------|------------------------| +|SSD1306 |128x32 |AVR |Primary support | +|SSD1306 |128x64 |AVR |Verified working | +|SSD1306 |128x32 |Arm | | +|SSD1306 |128x64 |Arm |Verified working | +|SH1106 |128x64 |AVR |No scrolling | +|SH1107 |64x128 |AVR |No scrolling | +|SH1107 |64x128 |Arm |No scrolling | +|SH1107 |128x128|Arm |No scrolling | + +Hardware configurations using Arm-based microcontrollers or different sizes of OLED modules may be compatible, but are untested. + +## Usage + +To enable the OLED feature, there are two steps. First, when compiling your keyboard, you'll need to add the following to your `rules.mk`: + +```make +OLED_ENABLE = yes +``` + +## OLED type + +|OLED Driver |Supported Device | +|-------------------|------------------------------------| +|`ssd1306` (default)|For both SSD1306, SH1106, and SH1107| + +e.g. +```make +OLED_DRIVER = ssd1306 +``` + +|OLED Transport | | +|---------------|------------------------------------------------| +|`i2c` (default)| Uses I2C for communication with the OLED panel | +|`spi` | Uses SPI for communication with the OLED panel | + +e.g. +```make +OLED_TRANSPORT = i2c +``` + +Then in your `keymap.c` file, implement the OLED task call. This example assumes your keymap has three layers named `_QWERTY`, `_FN` and `_ADJ`: + +```c +#ifdef OLED_ENABLE +bool oled_task_user(void) { + // Host Keyboard Layer Status + oled_write_P(PSTR("Layer: "), false); + + switch (get_highest_layer(layer_state)) { + case _QWERTY: + oled_write_P(PSTR("Default\n"), false); + break; + case _FN: + oled_write_P(PSTR("FN\n"), false); + break; + case _ADJ: + oled_write_P(PSTR("ADJ\n"), false); + break; + default: + // Or use the write_ln shortcut over adding '\n' to the end of your string + oled_write_ln_P(PSTR("Undefined"), false); + } + + // Host Keyboard LED Status + led_t led_state = host_keyboard_led_state(); + oled_write_P(led_state.num_lock ? PSTR("NUM ") : PSTR(" "), false); + oled_write_P(led_state.caps_lock ? PSTR("CAP ") : PSTR(" "), false); + oled_write_P(led_state.scroll_lock ? PSTR("SCR ") : PSTR(" "), false); + + return false; +} +#endif +``` + +## Logo Example + +In the default font, certain ranges of characters are reserved for a QMK logo. To render this logo to the OLED 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 + }; + + oled_write_P(qmk_logo, false); +} + +bool oled_task_user(void) { + render_logo(); + return false; +} +``` + +::: tip +The default font file is located at `drivers/oled/glcdfont.c` and its location can be overwritten with the `OLED_FONT_H` configuration option. Font file content can be edited with external tools such as [Helix Font Editor](https://helixfonteditor.netlify.app/) and [Logo Editor](https://joric.github.io/qle/). +::: + +## Buffer Read Example +For some purposes, you may need to read the current state of the OLED display +buffer. The `oled_read_raw` function can be used to safely read bytes from the +buffer. + +In this example, calling `fade_display` in the `oled_task_user` function will +slowly fade away whatever is on the screen by turning random pixels black 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 + oled_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 = oled_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) { + oled_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 OLED 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 OLED_ENABLE +oled_rotation_t oled_init_user(oled_rotation_t rotation) { + if (!is_keyboard_master()) { + return OLED_ROTATION_180; // flips the display 180 degrees if offhand + } + + return rotation; +} + +bool oled_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 + oled_scroll_left(); // Turns on scrolling + } + return false; +} +#endif +``` + +Render a message before booting into bootloader mode. +```c +void oled_render_boot(bool bootloader) { + oled_clear(); + for (int i = 0; i < 16; i++) { + oled_set_cursor(0, i); + if (bootloader) { + oled_write_P(PSTR("Awaiting New Firmware "), false); + } else { + oled_write_P(PSTR("Rebooting "), false); + } + } + + oled_render_dirty(true); +} + +bool shutdown_user(bool jump_to_bootloader) { + oled_render_boot(jump_to_bootloader); +} + +``` + +## Basic Configuration + +These configuration options should be placed in `config.h`. Example: +```c +#define OLED_BRIGHTNESS 128 +``` + +|Define |Default |Description | +|---------------------------|-------------------------------|---------------------------------------------------------------------------------------------------------------------| +|`OLED_BRIGHTNESS` |`255` |The default brightness level of the OLED, from 0 to 255. | +|`OLED_COLUMN_OFFSET` |`0` |Shift output to the right this many pixels.
Useful for 128x64 displays centered on a 132x64 SH1106 IC. | +|`OLED_DISPLAY_CLOCK` |`0x80` |Set the display clock divide ratio/oscillator frequency. | +|`OLED_FONT_H` |`"glcdfont.c"` |The font code file to use for custom fonts | +|`OLED_FONT_START` |`0` |The starting character index for custom fonts | +|`OLED_FONT_END` |`223` |The ending character index for custom fonts | +|`OLED_FONT_WIDTH` |`6` |The font width | +|`OLED_FONT_HEIGHT` |`8` |The font height (untested) | +|`OLED_IC` |`OLED_IC_SSD1306` |Set to `OLED_IC_SH1106` or `OLED_IC_SH1107` if the corresponding controller chip is used. | +|`OLED_FADE_OUT` |*Not defined* |Enables fade out animation. Use together with `OLED_TIMEOUT`. | +|`OLED_FADE_OUT_INTERVAL` |`0` |The speed of fade out animation, from 0 to 15. Larger values are slower. | +|`OLED_SCROLL_TIMEOUT` |`0` |Scrolls the OLED screen after 0ms of OLED inactivity. Helps reduce OLED Burn-in. Set to 0 to disable. | +|`OLED_SCROLL_TIMEOUT_RIGHT`|*Not defined* |Scroll timeout direction is right when defined, left when undefined. | +|`OLED_TIMEOUT` |`60000` |Turns off the OLED screen after 60000ms of screen update inactivity. Helps reduce OLED Burn-in. Set to 0 to disable. | +|`OLED_UPDATE_INTERVAL` |`0` (`50` for split keyboards) |Set the time interval for updating the OLED display in ms. This will improve the matrix scan rate. | +|`OLED_UPDATE_PROCESS_LIMIT`|`1` |Set the number of dirty blocks to render per loop. Increasing may degrade performance. | + +### I2C Configuration +|Define |Default |Description | +|---------------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------| +|`OLED_DISPLAY_ADDRESS` |`0x3C` |The i2c address of the OLED Display | + +### SPI Configuration + +|Define |Default |Description | +|---------------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------| +|`OLED_DC_PIN` | Required |The pin used for the DC connection of the OLED Display. | +|`OLED_CS_PIN` | Required |The pin used for the CS connection of the OLED Display. | +|`OLED_RST_PIN` | *Not defined* |The pin used for the RST connection of the OLED Display (may be left undefined if the RST pin is not connected). | +|`OLED_SPI_MODE` |`3` (default) |The SPI Mode for the OLED Display (not typically changed). | +|`OLED_SPI_DIVISOR` |`2` (default) |The SPI Multiplier to use for the OLED Display. | + +## 128x64 & Custom sized OLED Displays + + The default display size for this feature is 128x32, and the defaults are set with that in mind. However, there are a number of additional presets for common sizes that we have added. You can define one of these values to use the presets. If your display doesn't match one of these presets, you can define `OLED_DISPLAY_CUSTOM` to manually specify all of the values. + +|Define |Default |Description | +|----------------------|---------------|---------------------------------------------------------------------------------------------------------------------------------------| +|`OLED_DISPLAY_128X64` |*Not defined* |Changes the display defines for use with 128x64 displays. | +|`OLED_DISPLAY_64X32` |*Not defined* |Changes the display defines for use with 64x32 displays. | +|`OLED_DISPLAY_64X48` |*Not defined* |Changes the display defines for use with 64x48 displays. | +|`OLED_DISPLAY_64X128` |*Not defined* |Changes the display defines for use with 64x128 displays. | +|`OLED_DISPLAY_128X128`|*Not defined* |Changes the display defines for use with 128x128 displays. | +|`OLED_DISPLAY_CUSTOM` |*Not defined* |Changes the display defines for use with custom displays.
Requires user to implement the below defines. | + +::: warning +64x128 and 128x128 displays default to the SH1107 IC type, as these heights are not supported by the other IC types. +::: + +|Define |Default |Description | +| --------------------|---------------|----------------------------------------------------------------------------------------------------------------------------------------| +|`OLED_DISPLAY_WIDTH` |`128` |The width of the OLED display. | +|`OLED_DISPLAY_HEIGHT`|`32` |The height of the OLED display. | +|`OLED_MATRIX_SIZE` |`512` |The local buffer size to allocate.
`(OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH)`. | +|`OLED_BLOCK_TYPE` |`uint16_t` |The unsigned integer type to use for dirty rendering. | +|`OLED_BLOCK_COUNT` |`16` |The number of blocks the display is divided into for dirty rendering.
`(sizeof(OLED_BLOCK_TYPE) * 8)`. | +|`OLED_BLOCK_SIZE` |`32` |The size of each block for dirty rendering
`(OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)`. | +|`OLED_COM_PINS` |`COM_PINS_SEQ` |How the SSD1306 chip maps it's memory to display.
Options are `COM_PINS_SEQ`, `COM_PINS_ALT`, `COM_PINS_SEQ_LR`, & `COM_PINS_ALT_LR`.| +|`OLED_COM_PIN_COUNT` |*Not defined* |Number of COM pins supported by the controller.
If not defined, the value appropriate for the defined `OLED_IC` is used. | +|`OLED_COM_PIN_OFFSET`|`0` |Number of the first COM pin used by the OLED matrix. | +|`OLED_SOURCE_MAP` |`{ 0, ... N }` |Precalculated source array to use for mapping source buffer to target OLED memory in 90 degree rendering. | +|`OLED_TARGET_MAP` |`{ 24, ... N }`|Precalculated target array to use for mapping source buffer to target OLED memory in 90 degree rendering. | + +### 90 Degree Rotation - Technical Mumbo Jumbo + +```c +// OLED Rotation enum values are flags +typedef enum { + OLED_ROTATION_0 = 0, + OLED_ROTATION_90 = 1, + OLED_ROTATION_180 = 2, + OLED_ROTATION_270 = 3, // OLED_ROTATION_90 | OLED_ROTATION_180 +} oled_rotation_t; +``` + +OLED displays driven by SSD1306, SH1106 or SH1107 drivers only natively support in hardware 0 degree and 180 degree rendering. This feature is done in software and not free. Using this feature will increase the time to calculate what data to send over i2c to the OLED. If you are strapped for cycles, this can cause keycodes to not register. In testing however, the rendering time on an ATmega32U4 board only went from 2ms to 5ms and keycodes not registering was only noticed once we hit 15ms. + +90 degree rotation is achieved by using bitwise operations to rotate each 8 block of memory and uses two precalculated arrays to remap buffer memory to OLED memory. The memory map defines are precalculated for remap performance and are calculated based on the display height, width, and block size. For example, in the 128x32 implementation with a `uint8_t` block type, we have a 64 byte block size. This gives us eight 8 byte blocks that need to be rotated and rendered. The OLED renders horizontally two 8 byte blocks before moving down a page, e.g: + +| | | | | | | +|---|---|---|---|---|---| +| 0 | 1 | | | | | +| 2 | 3 | | | | | +| 4 | 5 | | | | | +| 6 | 7 | | | | | + +However the local buffer is stored as if it was Height x Width display instead of Width x Height, e.g: + +| | | | | | | +|---|---|---|---|---|---| +| 3 | 7 | | | | | +| 2 | 6 | | | | | +| 1 | 5 | | | | | +| 0 | 4 | | | | | + +So those precalculated arrays just index the memory offsets in the order in which each one iterates its data. + +Rotation on SH1106 and SH1107 is noticeably less efficient than on SSD1306, because these controllers do not support the “horizontal addressing mode”, which allows transferring the data for the whole rotated block at once; instead, separate address setup commands for every page in the block are required. The screen refresh time for SH1107 is therefore about 45% higher than for a same size screen with SSD1306 when using STM32 MCUs (on AVR the slowdown is about 20%, because the code which actually rotates the bitmap consumes more time). + +## OLED API + +```c +// OLED Rotation enum values are flags +typedef enum { + OLED_ROTATION_0 = 0, + OLED_ROTATION_90 = 1, + OLED_ROTATION_180 = 2, + OLED_ROTATION_270 = 3, // OLED_ROTATION_90 | OLED_ROTATION_180 +} oled_rotation_t; + +// Initialize the oled display, rotating the rendered output based on the define passed in. +// Returns true if the OLED was initialized successfully +bool oled_init(oled_rotation_t rotation); + +// Called at the start of oled_init, weak function overridable by the user +// rotation - the value passed into oled_init +// Return new oled_rotation_t if you want to override default rotation +oled_rotation_t oled_init_kb(oled_rotation_t rotation); +oled_rotation_t oled_init_user(oled_rotation_t rotation); + +// Send commands/data to screen +bool oled_send_cmd(const uint8_t *data, uint16_t size); +bool oled_send_cmd_P(const uint8_t *data, uint16_t size); +bool oled_send_data(const uint8_t *data, uint16_t size); + +// Clears the display buffer, resets cursor position to 0, and sets the buffer to dirty for rendering +void oled_clear(void); + +// Alias to oled_render_dirty to avoid a change in api. +#define oled_render() oled_render_dirty(false) + +// Renders all dirty blocks to the display at one time or a subset depending on the value of +// all. +void oled_render_dirty(bool all); + +// Moves cursor to character position indicated by column and line, wraps if out of bounds +// Max column denoted by 'oled_max_chars()' and max lines by 'oled_max_lines()' functions +void oled_set_cursor(uint8_t col, uint8_t line); + +// Advances the cursor to the next page, writing ' ' if true +// Wraps to the beginning when out of bounds +void oled_advance_page(bool clearPageRemainder); + +// Moves the cursor forward 1 character length +// Advance page if there is not enough room for the next character +// Wraps to the beginning when out of bounds +void oled_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 oled_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 oled_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 oled_write_ln(const char *data, bool invert); + +// Pans the buffer to the right (or left by passing true) by moving contents of the buffer +// Useful for moving the screen in preparation for new drawing +void oled_pan(bool left); + +// Returns a pointer to the requested start index in the buffer plus remaining +// buffer length as struct +oled_buffer_reader_t oled_read_raw(uint16_t start_index); + +// Writes a string to the buffer at current cursor position +void oled_write_raw(const char *data, uint16_t size); + +// Writes a single byte into the buffer at the specified index +void oled_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 oled_write_pixel(uint8_t x, uint8_t y, bool on); + +#if defined(__AVR__) +// Writes a PROGMEM string to the buffer at current cursor position +// Advances the cursor while writing, inverts the pixels if true +// Remapped to call 'void oled_write(const char *data, bool invert);' on ARM +void oled_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 oled_write_ln(const char *data, bool invert);' on ARM +void oled_write_ln_P(const char *data, bool invert); + +// Writes a PROGMEM string to the buffer at current cursor position +void oled_write_raw_P(const char *data, uint16_t size); +#else +# define oled_write_P(data, invert) oled_write(data, invert) +# define oled_write_ln_P(data, invert) oled_write_ln(data, invert) +# define oled_write_raw_P(data, size) oled_write_raw(data, size) +#endif // defined(__AVR__) + +// Can be used to manually turn on the screen if it is off +// Returns true if the screen was on or turns on +bool oled_on(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 oled_off(void); + +// Returns true if the oled is currently on, false if it is +// not +bool is_oled_on(void); + +// Sets the brightness level of the display +uint8_t oled_set_brightness(uint8_t level); + +// Gets the current brightness level of the display +uint8_t oled_get_brightness(void); + +// Basically it's oled_render, but with timeout management and oled_task_user calling! +void oled_task(void); + +// Called at the start of oled_task, weak function overridable by the user +bool oled_task_kb(void); +bool oled_task_user(void); + +// Set the specific 8 lines rows of the screen to scroll. +// 0 is the default for start, and 7 for end, which is the entire +// height of the screen. For 128x32 screens, rows 4-7 are not used. +void oled_scroll_set_area(uint8_t start_line, uint8_t end_line); + +// Sets scroll speed, 0-7, fastest to slowest. Default is three. +// Does not take effect until scrolling is either started or restarted +// the ssd1306 supports 8 speeds with the delay +// listed below between each frame of the scrolling effect +// 0=2, 1=3, 2=4, 3=5, 4=25, 5=64, 6=128, 7=256 +void oled_scroll_set_speed(uint8_t speed); + +// Begin scrolling the entire display right +// Returns true if the screen was scrolling or starts scrolling +// NOTE: display contents cannot be changed while scrolling +bool oled_scroll_right(void); + +// Begin scrolling the entire display left +// Returns true if the screen was scrolling or starts scrolling +// NOTE: display contents cannot be changed while scrolling +bool oled_scroll_left(void); + +// Turns off display scrolling +// Returns true if the screen was not scrolling or stops scrolling +bool oled_scroll_off(void); + +// Returns true if the oled is currently scrolling, false if it is +// not +bool is_oled_scrolling(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); + +// Returns the maximum number of lines that will fit on the OLED +uint8_t oled_max_lines(void); +``` + +::: warning +Scrolling is unsupported on the SH1106 and SH1107. +::: + +::: warning +Scrolling does not work properly on the SSD1306 if the display width is smaller than 128. +::: + +## SSD1306.h Driver Conversion Guide + +|Old API |Recommended New API | +|-------------------------|---------------------------------| +|`struct CharacterMatrix` |*removed - delete all references*| +|`iota_gfx_init` |`oled_init` | +|`iota_gfx_on` |`oled_on` | +|`iota_gfx_off` |`oled_off` | +|`iota_gfx_flush` |`oled_render` | +|`iota_gfx_write_char` |`oled_write_char` | +|`iota_gfx_write` |`oled_write` | +|`iota_gfx_write_P` |`oled_write_P` | +|`iota_gfx_clear_screen` |`oled_clear` | +|`matrix_clear` |*removed - delete all references*| +|`matrix_write_char_inner`|`oled_write_char` | +|`matrix_write_char` |`oled_write_char` | +|`matrix_write` |`oled_write` | +|`matrix_write_ln` |`oled_write_ln` | +|`matrix_write_P` |`oled_write_P` | +|`matrix_write_ln_P` |`oled_write_ln_P` | +|`matrix_render` |`oled_render` | +|`iota_gfx_task` |`oled_task` | +|`iota_gfx_task_user` |`oled_task_user` | diff --git a/docs/features/os_detection.md b/docs/features/os_detection.md new file mode 100644 index 0000000000..d0556d2549 --- /dev/null +++ b/docs/features/os_detection.md @@ -0,0 +1,132 @@ +# OS Detection + +This feature makes a best guess at the host OS based on OS specific behavior during USB setup. It may not always get the correct OS, and shouldn't be relied on as for critical functionality. + +Using it you can have OS specific key mappings or combos which work differently on different devices. + +It is available for keyboards which use ChibiOS, LUFA and V-USB. + +## Usage + +In your `rules.mk` add: + +```make +OS_DETECTION_ENABLE = yes +``` + +It will automatically include the required headers file. +It declares `os_variant_t detected_host_os(void);` which you can call to get detected OS. + +It returns one of the following values: + +```c +enum { + OS_UNSURE, + OS_LINUX, + OS_WINDOWS, + OS_MACOS, + OS_IOS, +} os_variant_t; +``` + +::: tip +Note that it takes some time after firmware is booted to detect the OS. +::: +This time is quite short, probably hundreds of milliseconds, but this data may be not ready in keyboard and layout setup functions which run very early during firmware startup. + +## Callbacks {#callbacks} + +If you want to perform custom actions when the OS is detected, then you can use the `process_detected_host_os_kb` function on the keyboard level source file, or `process_detected_host_os_user` function in the user `keymap.c`. + +```c +bool process_detected_host_os_kb(os_variant_t detected_os) { + if (!process_detected_host_os_user(detected_os)) { + return false; + } + switch (detected_os) { + case OS_MACOS: + case OS_IOS: + rgb_matrix_set_color_all(RGB_WHITE); + break; + case OS_WINDOWS: + rgb_matrix_set_color_all(RGB_BLUE); + break; + case OS_LINUX: + rgb_matrix_set_color_all(RGB_ORANGE); + break; + case OS_UNSURE: + rgb_matrix_set_color_all(RGB_RED); + break; + } + + return true; +} +``` + +## OS detection stability + +The OS detection is currently handled while the USB device descriptor is being assembled. +The process is done in steps, generating a number of intermediate results until it stabilizes. +We therefore resort to debouncing the result until it has been stable for a given amount of milliseconds. +This amount can be configured, in case your board is not stable within the default debouncing time of 200ms. + +## KVM and USB switches + +Some KVM and USB switches may not trigger the USB controller on the keyboard to fully reset upon switching machines. +If your keyboard does not redetect the OS in this situation, you can force the keyboard to reset when the USB initialization event is detected, forcing the USB controller to be reconfigured. + +## Configuration Options + +* `#define OS_DETECTION_DEBOUNCE 200` + * defined the debounce time for OS detection, in milliseconds +* `#define OS_DETECTION_KEYBOARD_RESET` + * enables the keyboard reset upon a USB device reinitilization, such as switching devices on some KVMs + +## Debug + +If OS is guessed incorrectly, you may want to collect data about USB setup packets to refine the detection logic. + +To do so in your `config.h` add: + +```c +#define OS_DETECTION_DEBUG_ENABLE +``` + +And in your `rules.mk` add: + +```make +CONSOLE_ENABLE = yes +``` + +And also include `"os_detection.h"` in your `keymap.c`. + +Then you can define custom keycodes to store data about USB setup packets in EEPROM (persistent memory) and to print it later on host where you can run `qmk console`: + +```c +enum custom_keycodes { + STORE_SETUPS = SAFE_RANGE, + PRINT_SETUPS, +}; + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case STORE_SETUPS: + if (record->event.pressed) { + store_setups_in_eeprom(); + } + return false; + case PRINT_SETUPS: + if (record->event.pressed) { + print_stored_setups(); + } + return false; + } +} +``` + +Then please open an issue on Github with this information and tell what OS was not detected correctly and if you have any intermediate devices between keyboard and your computer. + + +## Credits + +Original idea is coming from [FingerprintUSBHost](https://github.com/keyboardio/FingerprintUSBHost) project. diff --git a/docs/features/pointing_device.md b/docs/features/pointing_device.md new file mode 100644 index 0000000000..a6bf521a18 --- /dev/null +++ b/docs/features/pointing_device.md @@ -0,0 +1,928 @@ +# Pointing Device {#pointing-device} + +Pointing Device is a generic name for a feature intended to be generic: moving the system pointer around. There are certainly other options for it - like mousekeys - but this aims to be easily modifiable and hardware driven. You can implement custom keys to control functionality, or you can gather information from other peripherals and insert it directly here - let QMK handle the processing for you. + +To enable Pointing Device, add the following line in your rules.mk and specify one of the driver options below. + +```make +POINTING_DEVICE_ENABLE = yes +``` + +## Sensor Drivers + +There are a number of sensors that are supported by default. Note that only one sensor can be enabled by `POINTING_DEVICE_DRIVER` at a time. If you need to enable more than one sensor, then you need to implement it manually, using the `custom` driver. + +### ADNS 5050 Sensor + +To use the ADNS 5050 sensor, add this to your `rules.mk` + +```make +POINTING_DEVICE_DRIVER = adns5050 +``` + +The ADNS 5050 sensor uses a serial type protocol for communication, and requires an additional light source. + +| Setting (`config.h`) | Description | Default | +| -------------------- | ------------------------------------------------------------------ | -------------------------- | +| `ADNS5050_SCLK_PIN` | (Required) The pin connected to the clock pin of the sensor. | `POINTING_DEVICE_SCLK_PIN` | +| `ADNS5050_SDIO_PIN` | (Required) The pin connected to the data pin of the sensor. | `POINTING_DEVICE_SDIO_PIN` | +| `ADNS5050_CS_PIN` | (Required) The pin connected to the Chip Select pin of the sensor. | `POINTING_DEVICE_CS_PIN` | + + + +The CPI range is 125-1375, in increments of 125. Defaults to 500 CPI. + +### ADNS 9800 Sensor + +To use the ADNS 9800 sensor, add this to your `rules.mk` + +```make +POINTING_DEVICE_DRIVER = adns9800 +``` + +The ADNS 9800 is an SPI driven optical sensor, that uses laser output for surface tracking. + +| Setting (`config.h`) | Description | Default | +| ----------------------- | ---------------------------------------------------------------------- | ------------------------ | +| `ADNS9800_CLOCK_SPEED` | (Optional) Sets the clock speed that the sensor runs at. | `2000000` | +| `ADNS9800_SPI_LSBFIRST` | (Optional) Sets the Least/Most Significant Byte First setting for SPI. | `false` | +| `ADNS9800_SPI_MODE` | (Optional) Sets the SPI Mode for the sensor. | `3` | +| `ADNS9800_SPI_DIVISOR` | (Optional) Sets the SPI Divisor used for SPI communication. | _varies_ | +| `ADNS9800_CS_PIN` | (Required) Sets the Chip Select pin connected to the sensor. | `POINTING_DEVICE_CS_PIN` | + + +The CPI range is 800-8200, in increments of 200. Defaults to 1800 CPI. + +### Analog Joystick + +To use an analog joystick to control the pointer, add this to your `rules.mk` + +```make +POINTING_DEVICE_DRIVER = analog_joystick +``` + +The Analog Joystick is an analog (ADC) driven sensor. There are a variety of joysticks that you can use for this. + +| Setting (`config.h`) | Description | Default | +| --------------------------------- | -------------------------------------------------------------------------- | ------------- | +| `ANALOG_JOYSTICK_X_AXIS_PIN` | (Required) The pin used for the vertical/X axis. | _not defined_ | +| `ANALOG_JOYSTICK_Y_AXIS_PIN` | (Required) The pin used for the horizontal/Y axis. | _not defined_ | +| `ANALOG_JOYSTICK_AXIS_MIN` | (Optional) Sets the lower range to be considered movement. | `0` | +| `ANALOG_JOYSTICK_AXIS_MAX` | (Optional) Sets the upper range to be considered movement. | `1023` | +| `ANALOG_JOYSTICK_AUTO_AXIS` | (Optional) Sets ranges to be considered movement automatically. | _not defined_ | +| `ANALOG_JOYSTICK_SPEED_REGULATOR` | (Optional) The divisor used to slow down movement. (lower makes it faster) | `20` | +| `ANALOG_JOYSTICK_READ_INTERVAL` | (Optional) The interval in milliseconds between reads. | `10` | +| `ANALOG_JOYSTICK_SPEED_MAX` | (Optional) The maximum value used for motion. | `2` | +| `ANALOG_JOYSTICK_CLICK_PIN` | (Optional) The pin wired up to the press switch of the analog stick. | _not defined_ | +| `ANALOG_JOYSTICK_WEIGHTS` | (Optional) Use custom weights for lever positions. | _not defined_ | +| `ANALOG_JOYSTICK_CUTOFF` | (Optional) Cut off movement when joystick returns to start position. | _not defined_ | + +If `ANALOG_JOYSTICK_AUTO_AXIS` is used, then `ANALOG_JOYSTICK_AXIS_MIN` and `ANALOG_JOYSTICK_AXIS_MAX` are ignored. + +By default analog joystick implementation uses `x^2` weighting for lever positions. `ANALOG_JOYSTICK_WEIGHTS` allows to experiment with different configurations that might feel better. + +E.g. This is weights for `((x-0.4)^3+0.064)/0.282`: + +```c +#define ANALOG_JOYSTICK_WEIGHTS {0,2,4,5,7,8,9,10,12,13,14,15,15,16,17,18,18,19,19,20,20,21,21,21,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,25,25,25,26,26,26,27,28,28,29,29,30,31,32,33,34,35,36,37,38,40,41,43,44,46,48,49,51,53,56,58,60,62,65,68,70,73,76,79,82,85,89,92,96,100} +``` + +You can use following JS code to generate weights for different formulas: + +```js +JSON.stringify(Array.from(Array(101).keys()).map(x => Math.ceil((((x/100-0.4)**3+0.064)/0.282*100)))) +``` + +### Azoteq IQS5XX Trackpad + +To use a Azoteq IQS5XX trackpad, add this to your `rules.mk`: + +```make +POINTING_DEVICE_DRIVER = azoteq_iqs5xx +``` + +This supports the IQS525, IQS550 and IQS572 controllers, with the latter two being used in the TPS43 and TPS65 trackpads. + +#### Device settings + +Specific device profiles are provided which set the required values for dimensions and resolution. + +| Setting | Description | +| -------------------------------- | ---------------------------------------------------------- | +| `AZOTEQ_IQS5XX_TPS43` | (Pick One) Sets resolution/mm to TPS43 specifications. | +| `AZOTEQ_IQS5XX_TPS65` | (Pick One) Sets resolution/mm to TPS65 specifications. | + +::: tip +If using one of the above defines you can skip to gesture settings. +::: + +| Setting | Description | Default | +| -------------------------------- | ---------------------------------------------------------- | ------------- | +| `AZOTEQ_IQS5XX_WIDTH_MM` | (Required) Width of the trackpad sensor in millimeters. | _not defined_ | +| `AZOTEQ_IQS5XX_HEIGHT_MM` | (Required) Height of the trackpad sensor in millimeters. | _not defined_ | +| `AZOTEQ_IQS5XX_RESOLUTION_X` | (Optional) Specify X resolution for CPI calculation. | _not defined_ | +| `AZOTEQ_IQS5XX_RESOLUTION_Y` | (Optional) Specify Y resolution for CPI calculation. | _not defined_ | + +**`AZOTEQ_IQS5XX_RESOLUTION_X/Y`** fall back resolutions are provided within the driver based on controller model. + +| I2C Setting | Description | Default | +| ------------------------- | ------------------------------------------------------------------------------- | ------- | +| `AZOTEQ_IQS5XX_ADDRESS` | (Optional) Sets the I2C Address for the Azoteq trackpad | `0xE8` | +| `AZOTEQ_IQS5XX_TIMEOUT_MS`| (Optional) The timeout for i2c communication with in milliseconds. | `10` | + +#### Gesture settings + +| Setting | Description | Default | +| ----------------------------------------- | ------------------------------------------------------------------------------------ | ----------- | +| `AZOTEQ_IQS5XX_TAP_ENABLE` | (Optional) Enable single finger tap. (Left click) | `true` | +| `AZOTEQ_IQS5XX_TWO_FINGER_TAP_ENABLE` | (Optional) Enable two finger tap. (Right click) | `true` | +| `AZOTEQ_IQS5XX_PRESS_AND_HOLD_ENABLE` | (Optional) Emulates holding left click to select text. | `false` | +| `AZOTEQ_IQS5XX_SWIPE_X_ENABLE` | (Optional) Enable swipe gestures X+ (Mouse Button 5) / X- (Mouse Button 4) | `false` | +| `AZOTEQ_IQS5XX_SWIPE_Y_ENABLE` | (Optional) Enable swipe gestures Y+ (Mouse Button 3) / Y- (Mouse Button 6) | `false` | +| `AZOTEQ_IQS5XX_ZOOM_ENABLE` | (Optional) Enable zoom gestures Zoom Out (Mouse Button 7) / Zoom In (Mouse Button 8) | `false` | +| `AZOTEQ_IQS5XX_SCROLL_ENABLE` | (Optional) Enable scrolling using two fingers. | `true` | +| `AZOTEQ_IQS5XX_TAP_TIME` | (Optional) Maximum time in ms for tap to be registered. | `150` | +| `AZOTEQ_IQS5XX_TAP_DISTANCE` | (Optional) Maximum deviation in pixels before single tap is no longer valid. | `25` | +| `AZOTEQ_IQS5XX_HOLD_TIME` | (Optional) Minimum time in ms for press and hold. | `300` | +| `AZOTEQ_IQS5XX_SWIPE_INITIAL_TIME` | (Optional) Maximum time to travel initial distance before swipe is registered. | `150` | +| `AZOTEQ_IQS5XX_SWIPE_INITIAL_DISTANCE` | (Optional) Minimum travel in pixels before swipe is registered. | `300` | +| `AZOTEQ_IQS5XX_SWIPE_CONSECUTIVE_TIME` | (Optional) Maximum time to travel consecutive distance before swipe is registered. | `0` | +| `AZOTEQ_IQS5XX_SWIPE_CONSECUTIVE_DISTANCE`| (Optional) Minimum travel in pixels before a consecutive swipe is registered. | `2000` | +| `AZOTEQ_IQS5XX_SCROLL_INITIAL_DISTANCE` | (Optional) Minimum travel in pixels before scroll is registered. | `50` | +| `AZOTEQ_IQS5XX_ZOOM_INITIAL_DISTANCE` | (Optional) Minimum travel in pixels before zoom is registered. | `50` | +| `AZOTEQ_IQS5XX_ZOOM_CONSECUTIVE_DISTANCE` | (Optional) Maximum time to travel zoom distance before zoom is registered. | `25` | + +#### Rotation settings + +| Setting | Description | Default | +| ---------------------------- | ---------------------------------------------------------- | ------------- | +| `AZOTEQ_IQS5XX_ROTATION_90` | (Optional) Configures hardware for 90 degree rotation. | _not defined_ | +| `AZOTEQ_IQS5XX_ROTATION_180` | (Optional) Configures hardware for 180 degree rotation. | _not defined_ | +| `AZOTEQ_IQS5XX_ROTATION_270` | (Optional) Configures hardware for 270 degree rotation. | _not defined_ | + +### Cirque Trackpad + +To use the Cirque Trackpad sensor, add this to your `rules.mk`: + +```make +POINTING_DEVICE_DRIVER = cirque_pinnacle_i2c +``` + +or + +```make +POINTING_DEVICE_DRIVER = cirque_pinnacle_spi +``` + + +This supports the Cirque Pinnacle 1CA027 Touch Controller, which is used in the TM040040, TM035035 and the TM023023 trackpads. These are I2C or SPI compatible, and both configurations are supported. + +#### Common settings + +| Setting | Description | Default | +| ------------------------------------ | ---------------------------------------------------------- | ------------------------------------------- | +| `CIRQUE_PINNACLE_DIAMETER_MM` | (Optional) Diameter of the trackpad sensor in millimeters. | `40` | +| `CIRQUE_PINNACLE_ATTENUATION` | (Optional) Sets the attenuation of the sensor data. | `EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_4X` | +| `CIRQUE_PINNACLE_CURVED_OVERLAY` | (Optional) Applies settings tuned for curved overlay. | _not defined_ | +| `CIRQUE_PINNACLE_POSITION_MODE` | (Optional) Mode of operation. | _not defined_ | +| `CIRQUE_PINNACLE_SKIP_SENSOR_CHECK` | (Optional) Skips sensor presence check | _not defined_ | + +**`CIRQUE_PINNACLE_ATTENUATION`** is a measure of how much data is suppressed in regards to sensitivity. The higher the attenuation, the less sensitive the touchpad will be. + +Default attenuation is set to 4X, although if you are using a thicker overlay (such as the curved overlay) you will want a lower attenuation such as 2X. The possible values are: +* `EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_4X`: Least sensitive +* `EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_3X` +* `EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_2X` +* `EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_1X`: Most sensitive + +**`CIRQUE_PINNACLE_POSITION_MODE`** can be `CIRQUE_PINNACLE_ABSOLUTE_MODE` or `CIRQUE_PINNACLE_RELATIVE_MODE`. Modes differ in supported features/gestures. + +* `CIRQUE_PINNACLE_ABSOLUTE_MODE`: Reports absolute x, y, z (touch pressure) coordinates and up to 5 hw buttons connected to the trackpad +* `CIRQUE_PINNACLE_RELATIVE_MODE`: Reports x/y deltas, scroll and up to 3 buttons (2 of them can be from taps, see gestures) connected to trackpad. Supports taps on secondary side of split. Saves about 2k of flash compared to absolute mode with all features. + +| I2C Setting | Description | Default | +| ------------------------- | ------------------------------------------------------------------------------- | ------- | +| `CIRQUE_PINNACLE_ADDR` | (Required) Sets the I2C Address for the Cirque Trackpad | `0x2A` | +| `CIRQUE_PINNACLE_TIMEOUT` | (Optional) The timeout for i2c communication with the trackpad in milliseconds. | `20` | + +| SPI Setting | Description | Default | +| ------------------------------ | ---------------------------------------------------------------------- | ------------------------ | +| `CIRQUE_PINNACLE_CLOCK_SPEED` | (Optional) Sets the clock speed that the sensor runs at. | `1000000` | +| `CIRQUE_PINNACLE_SPI_LSBFIRST` | (Optional) Sets the Least/Most Significant Byte First setting for SPI. | `false` | +| `CIRQUE_PINNACLE_SPI_MODE` | (Optional) Sets the SPI Mode for the sensor. | `1` | +| `CIRQUE_PINNACLE_SPI_DIVISOR` | (Optional) Sets the SPI Divisor used for SPI communication. | _varies_ | +| `CIRQUE_PINNACLE_SPI_CS_PIN` | (Required) Sets the Chip Select pin connected to the sensor. | `POINTING_DEVICE_CS_PIN` | + +Default Scaling is 1024. Actual CPI depends on trackpad diameter. + +Also see the `POINTING_DEVICE_TASK_THROTTLE_MS`, which defaults to 10ms when using Cirque Pinnacle, which matches the internal update rate of the position registers (in standard configuration). Advanced configuration for pen/stylus usage might require lower values. + +#### Absolute mode settings + +| Setting | Description | Default | +|-----------------------------------------|-------------------------------------------------------------------------|-------------| +| `CIRQUE_PINNACLE_X_LOWER` | (Optional) The minimum reachable X value on the sensor. | `127` | +| `CIRQUE_PINNACLE_X_UPPER` | (Optional) The maximum reachable X value on the sensor. | `1919` | +| `CIRQUE_PINNACLE_Y_LOWER` | (Optional) The minimum reachable Y value on the sensor. | `63` | +| `CIRQUE_PINNACLE_Y_UPPER` | (Optional) The maximum reachable Y value on the sensor. | `1471` | +| `CIRQUE_PINNACLE_REACHABLE_CALIBRATION` | (Optional) Enable console messages to aide in calibrating above values. | not defined | + +#### Absolute mode gestures + +| Gesture Setting | Description | Default | +| ---------------------------------------------- | ------------------------------------------------------------------------------ | -------------------- | +| `CIRQUE_PINNACLE_TAP_ENABLE` | (Optional) Enable tap to click. This currently only works on the master side. | _not defined_ | +| `CIRQUE_PINNACLE_TAPPING_TERM` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` | +| `CIRQUE_PINNACLE_TOUCH_DEBOUNCE` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` | + +`POINTING_DEVICE_GESTURES_SCROLL_ENABLE` in this mode enables circular scroll. Touch originating in outer ring can trigger scroll by moving along the perimeter. Near side triggers vertical scroll and far side triggers horizontal scroll. + +Additionally, `POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE` is supported in this mode. + +#### Relative mode gestures + +| Gesture Setting (`config.h`) | Description | Default | +| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | +| `CIRQUE_PINNACLE_TAP_ENABLE` | (Optional) Enable tap to "left click". Works on both sides of a split keyboard. | _not defined_ | +| `CIRQUE_PINNACLE_SECONDARY_TAP_ENABLE` | (Optional) Tap in upper right corner (half of the finger needs to be outside of the trackpad) of the trackpad will result in "right click". `CIRQUE_PINNACLE_TAP_ENABLE` must be enabled. | _not defined_ | + +Tapping term and debounce are not configurable in this mode since it's handled by trackpad internally. + +`POINTING_DEVICE_GESTURES_SCROLL_ENABLE` in this mode enables side scroll. Touch originating on the right side can trigger vertical scroll (IntelliSense trackpad style). + +### PAW 3204 Sensor + +To use the paw 3204 sensor, add this to your `rules.mk` + +```make +POINTING_DEVICE_DRIVER = paw3204 +``` + +The paw 3204 sensor uses a serial type protocol for communication, and requires an additional light source. + +| Setting (`config.h`) | Description | Default | +| -------------------- |--------------------------------------------------------------- | -------------------------- | +| `PAW3204_SCLK_PIN` | (Required) The pin connected to the clock pin of the sensor. | `POINTING_DEVICE_SCLK_PIN` | +| `PAW3204_SDIO_PIN` | (Required) The pin connected to the data pin of the sensor. | `POINTING_DEVICE_SDIO_PIN` | + +The CPI range is 400-1600, with supported values of (400, 500, 600, 800, 1000, 1200 and 1600). Defaults to 1000 CPI. + +### Pimoroni Trackball + +To use the Pimoroni Trackball module, add this to your `rules.mk`: + +```make +POINTING_DEVICE_DRIVER = pimoroni_trackball +``` + +The Pimoroni Trackball module is a I2C based breakout board with an RGB enable trackball. + +| Setting (`config.h`) | Description | Default | +| ------------------------------------ | ---------------------------------------------------------------------------------- | ------- | +| `PIMORONI_TRACKBALL_ADDRESS` | (Required) Sets the I2C Address for the Pimoroni Trackball. | `0x0A` | +| `PIMORONI_TRACKBALL_TIMEOUT` | (Optional) The timeout for i2c communication with the trackball in milliseconds. | `100` | +| `PIMORONI_TRACKBALL_SCALE` | (Optional) The multiplier used to generate reports from the sensor. | `5` | +| `PIMORONI_TRACKBALL_DEBOUNCE_CYCLES` | (Optional) The number of scan cycles used for debouncing on the ball press. | `20` | +| `PIMORONI_TRACKBALL_ERROR_COUNT` | (Optional) Specifies the number of read/write errors until the sensor is disabled. | `10` | + +### PMW3320 Sensor + +To use the PMW3320 sensor, add this to your `rules.mk` + +```make +POINTING_DEVICE_DRIVER = pmw3320 +``` + +The PMW3320 sensor uses a serial type protocol for communication, and requires an additional light source (it could work without one, but expect it to be out of service early). + +| Setting | Description | Default | +| ------------------- | ------------------------------------------------------------------- | -------------------------- | +| `PMW3320_SCLK_PIN` | (Required) The pin connected to the clock pin of the sensor. | `POINTING_DEVICE_SCLK_PIN` | +| `PMW3320_SDIO_PIN` | (Required) The pin connected to the data pin of the sensor. | `POINTING_DEVICE_SDIO_PIN` | +| `PMW3320_CS_PIN` | (Required) The pin connected to the cable select pin of the sensor. | `POINTING_DEVICE_CS_PIN` | + +The CPI range is 500-3500, in increments of 250. Defaults to 1000 CPI. + +### PMW 3360 and PMW 3389 Sensor + +This drivers supports both the PMW 3360 and PMW 3389 sensor as well as multiple sensors of the same type _per_ controller, so 2 can be attached at the same side for split keyboards (or unsplit keyboards). + +To use the **PMW 3360** sensor, add this to your `rules.mk` + +```make +POINTING_DEVICE_DRIVER = pmw3360 +``` + +The CPI range is 100-12000, in increments of 100. Defaults to 1600 CPI. + +To use the **PMW 3389** sensor, add this to your `rules.mk` + +```make +POINTING_DEVICE_DRIVER = pmw3389 +``` + +The CPI range is 50-16000, in increments of 50. Defaults to 2000 CPI. + +Both PMW 3360 and PMW 3389 are SPI driven optical sensors, that use a built in IR LED for surface tracking. +If you have different CS wiring on each half you can use `PMW33XX_CS_PIN_RIGHT` or `PMW33XX_CS_PINS_RIGHT` in combination with `PMW33XX_CS_PIN` or `PMW33XX_CS_PINS` to configure both sides independently. If `_RIGHT` values aren't provided, they default to be the same as the left ones. + +| Setting (`config.h`) | Description | Default | +| ---------------------------- | ------------------------------------------------------------------------------------------- | ------------------------ | +| `PMW33XX_CS_PIN` | (Required) Sets the Chip Select pin connected to the sensor. | `POINTING_DEVICE_CS_PIN` | +| `PMW33XX_CS_PINS` | (Alternative) Sets the Chip Select pins connected to multiple sensors. | `{PMW33XX_CS_PIN}` | +| `PMW33XX_CS_PIN_RIGHT` | (Optional) Sets the Chip Select pin connected to the sensor on the right half. | `PMW33XX_CS_PIN` | +| `PMW33XX_CS_PINS_RIGHT` | (Optional) Sets the Chip Select pins connected to multiple sensors on the right half. | `{PMW33XX_CS_PIN_RIGHT}` | +| `PMW33XX_CPI` | (Optional) Sets counts per inch sensitivity of the sensor. | _varies_ | +| `PMW33XX_CLOCK_SPEED` | (Optional) Sets the clock speed that the sensor runs at. | `2000000` | +| `PMW33XX_SPI_DIVISOR` | (Optional) Sets the SPI Divisor used for SPI communication. | _varies_ | +| `PMW33XX_LIFTOFF_DISTANCE` | (Optional) Sets the lift off distance at run time | `0x02` | +| `ROTATIONAL_TRANSFORM_ANGLE` | (Optional) Allows for the sensor data to be rotated +/- 127 degrees directly in the sensor. | `0` | + +To use multiple sensors, instead of setting `PMW33XX_CS_PIN` you need to set `PMW33XX_CS_PINS` and also handle and merge the read from this sensor in user code. +Note that different (per sensor) values of CPI, speed liftoff, rotational angle or flipping of X/Y is not currently supported. + +```c +// in config.h: +#define PMW33XX_CS_PINS { B5, B6 } +// in keyboard.c: +#ifdef POINTING_DEVICE_ENABLE +void pointing_device_init_kb(void) { + pmw33xx_init(1); // index 1 is the second device. + pmw33xx_set_cpi(0, 800); // applies to first sensor + pmw33xx_set_cpi(1, 800); // applies to second sensor + pointing_device_init_user(); +} + +// Contains report from sensor #0 already, need to merge in from sensor #1 +report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { + pmw33xx_report_t report = pmw33xx_read_burst(1); + if (!report.motion.b.is_lifted && report.motion.b.is_motion) { +// From quantum/pointing_device_drivers.c +#define constrain_hid(amt) ((amt) < -127 ? -127 : ((amt) > 127 ? 127 : (amt))) + mouse_report.x = constrain_hid(mouse_report.x + report.delta_x); + mouse_report.y = constrain_hid(mouse_report.y + report.delta_y); + } + return pointing_device_task_user(mouse_report); +} +#endif + +``` + +### Custom Driver + +If you have a sensor type that isn't supported above, a custom option is available by adding the following to your `rules.mk` + +```make +POINTING_DEVICE_DRIVER = custom +``` + +Using the custom driver will require implementing the following functions: + +```c +void pointing_device_driver_init(void) {} +report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) { return mouse_report; } +uint16_t pointing_device_driver_get_cpi(void) { return 0; } +void pointing_device_driver_set_cpi(uint16_t cpi) {} +``` + +::: warning +Ideally, new sensor hardware should be added to `drivers/sensors/` and `quantum/pointing_device_drivers.c`, but there may be cases where it's very specific to the hardware. So these functions are provided, just in case. +::: + +## Common Configuration + +| Setting | Description | Default | +| ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------- | +| `MOUSE_EXTENDED_REPORT` | (Optional) Enables support for extended mouse reports. (-32767 to 32767, instead of just -127 to 127). | _not defined_ | +| `POINTING_DEVICE_ROTATION_90` | (Optional) Rotates the X and Y data by 90 degrees. | _not defined_ | +| `POINTING_DEVICE_ROTATION_180` | (Optional) Rotates the X and Y data by 180 degrees. | _not defined_ | +| `POINTING_DEVICE_ROTATION_270` | (Optional) Rotates the X and Y data by 270 degrees. | _not defined_ | +| `POINTING_DEVICE_INVERT_X` | (Optional) Inverts the X axis report. | _not defined_ | +| `POINTING_DEVICE_INVERT_Y` | (Optional) Inverts the Y axis report. | _not defined_ | +| `POINTING_DEVICE_MOTION_PIN` | (Optional) If supported, will only read from sensor if pin is active. | _not defined_ | +| `POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW` | (Optional) If defined then the motion pin is active-low. | _varies_ | +| `POINTING_DEVICE_TASK_THROTTLE_MS` | (Optional) Limits the frequency that the sensor is polled for motion. | _not defined_ | +| `POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE` | (Optional) Enable inertial cursor. Cursor continues moving after a flick gesture and slows down by kinetic friction. | _not defined_ | +| `POINTING_DEVICE_GESTURES_SCROLL_ENABLE` | (Optional) Enable scroll gesture. The gesture that activates the scroll is device dependent. | _not defined_ | +| `POINTING_DEVICE_CS_PIN` | (Optional) Provides a default CS pin, useful for supporting multiple sensor configs. | _not defined_ | +| `POINTING_DEVICE_SDIO_PIN` | (Optional) Provides a default SDIO pin, useful for supporting multiple sensor configs. | _not defined_ | +| `POINTING_DEVICE_SCLK_PIN` | (Optional) Provides a default SCLK pin, useful for supporting multiple sensor configs. | _not defined_ | + +::: warning +When using `SPLIT_POINTING_ENABLE` the `POINTING_DEVICE_MOTION_PIN` functionality is not supported and `POINTING_DEVICE_TASK_THROTTLE_MS` will default to `1`. Increasing this value will increase transport performance at the cost of possible mouse responsiveness. +::: + +The `POINTING_DEVICE_CS_PIN`, `POINTING_DEVICE_SDIO_PIN`, and `POINTING_DEVICE_SCLK_PIN` provide a convenient way to define a single pin that can be used for an interchangeable sensor config. This allows you to have a single config, without defining each device. Each sensor allows for this to be overridden with their own defines. + +::: warning +Any pointing device with a lift/contact status can integrate inertial cursor feature into its driver, controlled by `POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE`. e.g. PMW3360 can use Lift_Stat from Motion register. Note that `POINTING_DEVICE_MOTION_PIN` cannot be used with this feature; continuous polling of `get_report()` is needed to generate glide reports. +::: + +## Split Keyboard Configuration + +The following configuration options are only available when using `SPLIT_POINTING_ENABLE` see [data sync options](split_keyboard#data-sync-options). The rotation and invert `*_RIGHT` options are only used with `POINTING_DEVICE_COMBINED`. If using `POINTING_DEVICE_LEFT` or `POINTING_DEVICE_RIGHT` use the common configuration above to configure your pointing device. + +| Setting | Description | Default | +| ------------------------------------ | ----------------------------------------------------------------------------------------------------- | ------------- | +| `POINTING_DEVICE_LEFT` | Pointing device on the left side (Required - pick one only) | _not defined_ | +| `POINTING_DEVICE_RIGHT` | Pointing device on the right side (Required - pick one only) | _not defined_ | +| `POINTING_DEVICE_COMBINED` | Pointing device on both sides (Required - pick one only) | _not defined_ | +| `POINTING_DEVICE_ROTATION_90_RIGHT` | (Optional) Rotates the X and Y data by 90 degrees. | _not defined_ | +| `POINTING_DEVICE_ROTATION_180_RIGHT` | (Optional) Rotates the X and Y data by 180 degrees. | _not defined_ | +| `POINTING_DEVICE_ROTATION_270_RIGHT` | (Optional) Rotates the X and Y data by 270 degrees. | _not defined_ | +| `POINTING_DEVICE_INVERT_X_RIGHT` | (Optional) Inverts the X axis report. | _not defined_ | +| `POINTING_DEVICE_INVERT_Y_RIGHT` | (Optional) Inverts the Y axis report. | _not defined_ | + +::: warning +If there is a `_RIGHT` configuration option or callback, the [common configuration](pointing_device#common-configuration) option will work for the left. For correct left/right detection you should setup a [handedness option](split_keyboard#setting-handedness), `EE_HANDS` is usually a good option for an existing board that doesn't do handedness by hardware. +::: + + +## Callbacks and Functions + +| Function | Description | +| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| `pointing_device_init_kb(void)` | Callback to allow for keyboard level initialization. Useful for additional hardware sensors. | +| `pointing_device_init_user(void)` | Callback to allow for user level initialization. Useful for additional hardware sensors. | +| `pointing_device_task_kb(mouse_report)` | Callback that sends sensor data, so keyboard code can intercept and modify the data. Returns a mouse report. | +| `pointing_device_task_user(mouse_report)` | Callback that sends sensor data, so user code can intercept and modify the data. Returns a mouse report. | +| `pointing_device_handle_buttons(buttons, pressed, button)` | Callback to handle hardware button presses. Returns a `uint8_t`. | +| `pointing_device_get_cpi(void)` | Gets the current CPI/DPI setting from the sensor, if supported. | +| `pointing_device_set_cpi(uint16_t)` | Sets the CPI/DPI, if supported. | +| `pointing_device_get_report(void)` | Returns the current mouse report (as a `report_mouse_t` data structure). | +| `pointing_device_set_report(mouse_report)` | Sets the mouse report to the assigned `report_mouse_t` data structured passed to the function. | +| `pointing_device_send(void)` | Sends the current mouse report to the host system. Function can be replaced. | +| `has_mouse_report_changed(new_report, old_report)` | Compares the old and new `report_mouse_t` data and returns true only if it has changed. | +| `pointing_device_adjust_by_defines(mouse_report)` | Applies rotations and invert configurations to a raw mouse report. | + + +## Split Keyboard Callbacks and Functions + +The combined functions below are only available when using `SPLIT_POINTING_ENABLE` and `POINTING_DEVICE_COMBINED`. The 2 callbacks `pointing_device_task_combined_*` replace the single sided equivalents above. See the [combined pointing devices example](pointing_device#combined-pointing-devices) + +| Function | Description | +| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| `pointing_device_set_shared_report(mouse_report)` | Sets the shared mouse report to the assigned `report_mouse_t` data structured passed to the function. | +| `pointing_device_set_cpi_on_side(bool, uint16_t)` | Sets the CPI/DPI of one side, if supported. Passing `true` will set the left and `false` the right | +| `pointing_device_combine_reports(left_report, right_report)` | Returns a combined mouse_report of left_report and right_report (as a `report_mouse_t` data structure) | +| `pointing_device_task_combined_kb(left_report, right_report)` | Callback, so keyboard code can intercept and modify the data. Returns a combined mouse report. | +| `pointing_device_task_combined_user(left_report, right_report)` | Callback, so user code can intercept and modify. Returns a combined mouse report using `pointing_device_combine_reports` | +| `pointing_device_adjust_by_defines_right(mouse_report)` | Applies right side rotations and invert configurations to a raw mouse report. | + + +# Manipulating Mouse Reports + +The report_mouse_t (here "mouseReport") has the following properties: + +* `mouseReport.x` - this is a signed int from -127 to 127 (not 128, this is defined in USB HID spec) representing movement (+ to the right, - to the left) on the x axis. +* `mouseReport.y` - this is a signed int from -127 to 127 (not 128, this is defined in USB HID spec) representing movement (+ upward, - downward) on the y axis. +* `mouseReport.v` - this is a signed int from -127 to 127 (not 128, this is defined in USB HID spec) representing vertical scrolling (+ upward, - downward). +* `mouseReport.h` - this is a signed int from -127 to 127 (not 128, this is defined in USB HID spec) representing horizontal scrolling (+ right, - left). +* `mouseReport.buttons` - this is a uint8_t in which all 8 bits are used. These bits represent the mouse button state - bit 0 is mouse button 1, and bit 7 is mouse button 8. + +To manually manipulate the mouse reports outside of the `pointing_device_task_*` functions, you can use: + +* `pointing_device_get_report()` - Returns the current report_mouse_t that represents the information sent to the host computer +* `pointing_device_set_report(report_mouse_t mouse_report)` - Overrides and saves the report_mouse_t to be sent to the host computer +* `pointing_device_send()` - Sends the mouse report to the host and zeroes out the report. + +When the mouse report is sent, the x, y, v, and h values are set to 0 (this is done in `pointing_device_send()`, which can be overridden to avoid this behavior). This way, button states persist, but movement will only occur once. For further customization, both `pointing_device_init` and `pointing_device_task` can be overridden. + +Additionally, by default, `pointing_device_send()` will only send a report when the report has actually changed. This prevents it from continuously sending mouse reports, which will keep the host system awake. This behavior can be changed by creating your own `pointing_device_send()` function. + +Also, you use the `has_mouse_report_changed(new_report, old_report)` function to check to see if the report has changed. + +## Examples + +### Custom Mouse Keycode + +In this example, a custom key is used to click the mouse and scroll 127 units vertically and horizontally, then undo all of that when released - because that's a totally useful function. + +```c +case MS_SPECIAL: + report_mouse_t currentReport = pointing_device_get_report(); + if (record->event.pressed) { + currentReport.v = 127; + currentReport.h = 127; + currentReport.buttons |= MOUSE_BTN1; // this is defined in report.h + } else { + currentReport.v = -127; + currentReport.h = -127; + currentReport.buttons &= ~MOUSE_BTN1; + } + pointing_device_set_report(currentReport); + pointing_device_send(); + break; +``` + +Recall that the mouse report is set to zero (except the buttons) whenever it is sent, so the scrolling would only occur once in each case. + +### Drag Scroll or Mouse Scroll + +A very common implementation is to use the mouse movement to scroll instead of moving the cursor on the system. This uses the `pointing_device_task_user` callback to intercept and modify the mouse report before it's sent to the host system. + +```c +enum custom_keycodes { + DRAG_SCROLL = SAFE_RANGE, +}; + +bool set_scrolling = false; + +report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) { + if (set_scrolling) { + mouse_report.h = mouse_report.x; + mouse_report.v = mouse_report.y; + mouse_report.x = 0; + mouse_report.y = 0; + } + return mouse_report; +} + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + if (keycode == DRAG_SCROLL && record->event.pressed) { + set_scrolling = !set_scrolling; + } + return true; +} +``` + +This allows you to toggle between scrolling and cursor movement by pressing the DRAG_SCROLL key. + +### Advanced Drag Scroll + +Sometimes, like with the Cirque trackpad, you will run into issues where the scrolling may be too fast. + +Here is a slightly more advanced example of drag scrolling. You will be able to change the scroll speed based on the values in set in `SCROLL_DIVISOR_H` and `SCROLL_DIVISOR_V`. This bit of code is also set up so that instead of toggling the scrolling state with set_scrolling = !set_scrolling, the set_scrolling variable is set directly to record->event.pressed. This way, the drag scrolling will only be active while the DRAG_SCROLL button is held down. + +```c +enum custom_keycodes { + DRAG_SCROLL = SAFE_RANGE, +}; + +bool set_scrolling = false; + +// Modify these values to adjust the scrolling speed +#define SCROLL_DIVISOR_H 8.0 +#define SCROLL_DIVISOR_V 8.0 + +// Variables to store accumulated scroll values +float scroll_accumulated_h = 0; +float scroll_accumulated_v = 0; + +// Function to handle mouse reports and perform drag scrolling +report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) { + // Check if drag scrolling is active + if (set_scrolling) { + // Calculate and accumulate scroll values based on mouse movement and divisors + scroll_accumulated_h += (float)mouse_report.x / SCROLL_DIVISOR_H; + scroll_accumulated_v += (float)mouse_report.y / SCROLL_DIVISOR_V; + + // Assign integer parts of accumulated scroll values to the mouse report + mouse_report.h = (int8_t)scroll_accumulated_h; + mouse_report.v = (int8_t)scroll_accumulated_v; + + // Update accumulated scroll values by subtracting the integer parts + scroll_accumulated_h -= (int8_t)scroll_accumulated_h; + scroll_accumulated_v -= (int8_t)scroll_accumulated_v; + + // Clear the X and Y values of the mouse report + mouse_report.x = 0; + mouse_report.y = 0; + } + return mouse_report; +} + +// Function to handle key events and enable/disable drag scrolling +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case DRAG_SCROLL: + // Toggle set_scrolling when DRAG_SCROLL key is pressed or released + set_scrolling = record->event.pressed; + break; + default: + break; + } + return true; +} + +// Function to handle layer changes and disable drag scrolling when not in AUTO_MOUSE_DEFAULT_LAYER +layer_state_t layer_state_set_user(layer_state_t state) { + // Disable set_scrolling if the current layer is not the AUTO_MOUSE_DEFAULT_LAYER + if (get_highest_layer(state) != AUTO_MOUSE_DEFAULT_LAYER) { + set_scrolling = false; + } + return state; +} + +``` + + +## Split Examples + +The following examples make use the `SPLIT_POINTING_ENABLE` functionality and show how to manipulate the mouse report for a scrolling mode. + +### Single Pointing Device + +The following example will work with either `POINTING_DEVICE_LEFT` or `POINTING_DEVICE_RIGHT` and enables scrolling mode while on a particular layer. + +```c + +static bool scrolling_mode = false; + +layer_state_t layer_state_set_user(layer_state_t state) { + switch (get_highest_layer(state)) { + case _RAISE: // If we're on the _RAISE layer enable scrolling mode + scrolling_mode = true; + pointing_device_set_cpi(2000); + break; + default: + if (scrolling_mode) { // check if we were scrolling before and set disable if so + scrolling_mode = false; + pointing_device_set_cpi(8000); + } + break; + } + return state; +} + +report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) { + if (scrolling_mode) { + mouse_report.h = mouse_report.x; + mouse_report.v = mouse_report.y; + mouse_report.x = 0; + mouse_report.y = 0; + } + return mouse_report; +} +``` + +### Combined Pointing Devices + +The following example requires `POINTING_DEVICE_COMBINED` and sets the left side pointing device to scroll only. + +```c +void keyboard_post_init_user(void) { + pointing_device_set_cpi_on_side(true, 1000); //Set cpi on left side to a low value for slower scrolling. + pointing_device_set_cpi_on_side(false, 8000); //Set cpi on right side to a reasonable value for mousing. +} + +report_mouse_t pointing_device_task_combined_user(report_mouse_t left_report, report_mouse_t right_report) { + left_report.h = left_report.x; + left_report.v = left_report.y; + left_report.x = 0; + left_report.y = 0; + return pointing_device_combine_reports(left_report, right_report); +} +``` + +# Troubleshooting + +If you are having issues with pointing device drivers debug messages can be enabled that will give you insights in the inner workings. To enable these add to your keyboards `config.h` file: + +```c +#define POINTING_DEVICE_DEBUG +``` + +::: tip +The messages will be printed out to the `CONSOLE` output. For additional information, refer to [Debugging/Troubleshooting QMK](../faq_debug). +::: + + +--- +# Automatic Mouse Layer {#pointing-device-auto-mouse} + +When using a pointing device combined with a keyboard the mouse buttons are often kept on a separate layer from the default keyboard layer, which requires pressing or holding a key to change layers before using the mouse. To make this easier and more efficient an additional pointing device feature may be enabled that will automatically activate a target layer as soon as the pointing device is active _(in motion, mouse button pressed etc.)_ and deactivate the target layer after a set time. + +Additionally if any key that is defined as a mouse key is pressed then the layer will be held as long as the key is pressed and the timer will be reset on key release. When a non-mouse key is pressed then the layer is deactivated early _(with some exceptions see below)_. Mod, mod tap, and one shot mod keys are ignored _(i.e. don't hold or activate layer but do not deactivate the layer either)_ when sending a modifier keycode _(e.g. hold for mod tap)_ allowing for mod keys to be used with the mouse without activating the target layer when typing. + +All of the standard layer keys (tap toggling, toggle, toggle on, one_shot, layer tap, layer mod) that activate the current target layer are uniquely handled to ensure they behave as expected _(see layer key table below)_. The target layer that can be changed at any point during by calling the `set_auto_mouse_layer();` function. + +### Behaviour of Layer keys that activate the target layer +| Layer key as in `keymap.c` | Auto Mouse specific behaviour | +| -------------------------- | --------------------------------------------------------------------------------------------------------------------- | +| `MO()` | Treated as a mouse key holding the layer while pressed | +| `LT()` | When tapped will be treated as non mouse key and mouse key when held | +| `LM()` | Treated as a mouse key | +| `TG()` | Will set flag preventing target layer deactivation or removal until pressed again | +| `TO()` | Same as `TG()` | +| `TT()` | Treated as a mouse key when `tap.count < TAPPING_TOGGLE` and as `TG` when `tap.count == TAPPING_TOGGLE` | +| `DF()` | Skips auto mouse key processing similar to mod keys | +| `OSL()` | Skips, but if current one shot layer is the target layer then it will prevent target layer deactivation or removal | + + +## How to enable: + +```c +// in config.h: +#define POINTING_DEVICE_AUTO_MOUSE_ENABLE +// only required if not setting mouse layer elsewhere +#define AUTO_MOUSE_DEFAULT_LAYER + +// in keymap.c: +void pointing_device_init_user(void) { + set_auto_mouse_layer(); // only required if AUTO_MOUSE_DEFAULT_LAYER is not set to index of + set_auto_mouse_enable(true); // always required before the auto mouse feature will work +} +``` + +Because the auto mouse feature can be disabled/enabled during runtime and starts as disabled by default it must be enabled by calling `set_auto_mouse_enable(true);` somewhere in firmware before the feature will work. +_Note: for setting the target layer during initialization either setting `AUTO_MOUSE_DEFAULT_LAYER` in `config.h` or calling `set_auto_mouse_layer()` can be used._ + + +## How to Customize: + +There are a few ways to control the auto mouse feature with both `config.h` options and functions for controlling it during runtime. + +### `config.h` Options: +| Define | Description | Range | Units | Default | +| ----------------------------------- | --------------------------------------------------------------------- | :------------------: | :---------: | -------------------------: | +| `POINTING_DEVICE_AUTO_MOUSE_ENABLE` | (Required) Enables auto mouse layer feature | | _None_ | _Not defined_ | +| `AUTO_MOUSE_DEFAULT_LAYER` | (Optional) Index of layer to use as default target layer | 0 - `LAYER_MAX` | _`uint8_t`_ | `1` | +| `AUTO_MOUSE_TIME` | (Optional) Time layer remains active after activation | _ideally_ (250-1000) | _ms_ | `650 ms` | +| `AUTO_MOUSE_DELAY` | (Optional) Lockout time after non-mouse key is pressed | _ideally_ (100-1000) | _ms_ | `TAPPING_TERM` or `200 ms` | +| `AUTO_MOUSE_DEBOUNCE` | (Optional) Time delay from last activation to next update | _ideally_ (10 - 100) | _ms_ | `25 ms` | +| `AUTO_MOUSE_THRESHOLD` | (Optional) Amount of mouse movement required to switch layers | 0 - | _units_ | `10 units` | + +### Adding mouse keys + +While all default mouse keys and layer keys(for current mouse layer) are treated as mouse keys, additional Keyrecords can be added to mouse keys by adding them to the is_mouse_record_* stack. + +#### Callbacks for setting up additional key codes as mouse keys: +| Callback | Description | +| -------------------------------------------------------------------- | -------------------------------------------------- | +| `bool is_mouse_record_kb(uint16_t keycode, keyrecord_t* record)` | keyboard level callback for adding mouse keys | +| `bool is_mouse_record_user(uint16_t keycode, keyrecord_t* record)` | user/keymap level callback for adding mouse keys | + +##### To use the callback function to add mouse keys: + +The following code will cause the enter key and all of the arrow keys to be treated as mouse keys (hold target layer while they are pressed and reset active layer timer). +```c + +// in .c: +bool is_mouse_record_kb(uint16_t keycode, keyrecord_t* record) { + switch(keycode) { + case KC_ENT: + return true; + case KC_RIGHT ... KC_UP: + return true; + default: + return false; + } + return is_mouse_record_user(keycode, record); +} +``` + + +## Advanced control + +There are several functions that allow for more advanced interaction with the auto mouse feature allowing for greater control. + +### Functions to control auto mouse enable and target layer: +| Function | Description | Aliases | Return type | +| :--------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------- | --------------: | +| `set_auto_mouse_enable(bool enable)` | Enable or disable auto mouse (true:enable, false:disable) | | `void`(None) | +| `get_auto_mouse_enable(void)` | Return auto mouse enable state (true:enabled, false:disabled) | `AUTO_MOUSE_ENABLED` | `bool` | +| `set_auto_mouse_layer(uint8_t LAYER)` | Change/set the target layer for auto mouse | | `void`(None) | +| `get_auto_mouse_layer(void)` | Return auto mouse target layer index | `AUTO_MOUSE_TARGET_LAYER` | `uint8_t` | +| `remove_auto_mouse_layer(layer_state_t state, bool force)` | Return `state` with target layer removed if appropriate (ignore criteria if `force`) | | `layer_state_t` | +| `auto_mouse_layer_off(void)` | Disable target layer if appropriate will call (makes call to `layer_state_set`) | | `void`(None) | +| `auto_mouse_toggle(void)` | Toggle on/off target toggle state (disables layer deactivation when true) | | `void`(None) | +| `get_auto_mouse_toggle(void)` | Return value of toggling state variable | | `bool` | +| `set_auto_mouse_timeout(uint16_t timeout)` | Change/set the timeout for turing off the layer | | `void`(None) | +| `get_auto_mouse_timeout(void)` | Return the current timeout for turing off the layer | | `uint16_t` | +| `set_auto_mouse_debounce(uint16_t timeout)` | Change/set the debounce for preventing layer activation | | `void`(None) | +| `get_auto_mouse_debounce(void)` | Return the current debounce for preventing layer activation | | `uint8_t` | +| `is_auto_mouse_active(void)` | Returns the active state of the auto mouse layer (eg if the layer has been triggered)| | `bool` | +| `get_auto_mouse_key_tracker(void)` | Gets the current count for the auto mouse key tracker. | | `int8_t` | +| `set_auto_mouse_key_tracker(int8_t key_tracker)` | Sets/Overrides the current count for the auto mouse key tracker. | | `void`(None) | + +_NOTES:_ + - _Due to the nature of how some functions work, the `auto_mouse_trigger_reset`, and `auto_mouse_layer_off` functions should never be called in the `layer_state_set_*` stack as this can cause indefinite loops._ + - _It is recommended that `remove_auto_mouse_layer` is used in the `layer_state_set_*` stack of functions and `auto_mouse_layer_off` is used everywhere else_ + - _`remove_auto_mouse_layer(state, false)` or `auto_mouse_layer_off()` should be called before any instance of `set_auto_mouse_enabled(false)` or `set_auto_mouse_layer(layer)` to ensure that the target layer will be removed appropriately before disabling auto mouse or changing target to avoid a stuck layer_ + +### Functions for handling custom key events: +| Function | Description | Return type | +| :--------------------------------------------------------- | -------------------------------------------------------------------------------- | --------------: | +| `auto_mouse_keyevent(bool pressed)` | Auto mouse mouse key event (true: key down, false: key up) | `void`(None) | +| `auto_mouse_trigger_reset(bool pressed)` | Reset auto mouse status on key down and start delay timer (non-mouse key event) | `void`(None) | +| `auto_mouse_toggle(void)` | Toggle on/off target toggle state (disables layer deactivation when true) | `void`(None) | +| `get_auto_mouse_toggle(void)` | Return value of toggling state variable | `bool` | +_NOTE: Generally it would be preferable to use the `is_mouse_record_*` functions to add any additional keys that should act as mouse keys rather than adding `auto_mouse_keyevent(record.event->pressed)` to `process_records_*`_ + +### Advanced control examples + +#### Disable auto mouse on certain layers: + +The auto mouse feature can be disabled any time and this can be helpful if you want to disable the auto mouse feature under certain circumstances such as when particular layers are active. One issue however is the handling of the target layer, it needs to be removed appropriately **before** disabling auto mouse _(see notes under control functions above)_. The following function would disable the auto_mouse feature whenever the layers `_LAYER5` through `_LAYER7` are active as the top most layer _(ignoring target layer)_. + +```c +// in keymap.c: +layer_state_t layer_state_set_user(layer_state_t state) { + // checks highest layer other than target layer + switch(get_highest_layer(remove_auto_mouse_layer(state, true))) { + case _LAYER5 ... _LAYER7: + // remove_auto_mouse_target must be called to adjust state *before* setting enable + state = remove_auto_mouse_layer(state, false); + set_auto_mouse_enable(false); + break; + default: + set_auto_mouse_enable(true); + break; + } + // recommend that any code that makes adjustment based on auto mouse layer state would go here + return state; +} +``` + +#### Set different target layer when a particular layer is active: + +The below code will change the auto mouse layer target to `_MOUSE_LAYER_2` when `_DEFAULT_LAYER_2` is highest default layer state. + +*NOTE: that `auto_mouse_layer_off` is used here instead of `remove_auto_mouse_layer` as `default_layer_state_set_*` stack is separate from the `layer_state_set_*` stack*, if something similar was to be done in `layer_state_set_user`, `state = remove_auto_mouse_layer(state, false)` should be used instead. + +*ADDITIONAL NOTE: `AUTO_MOUSE_TARGET_LAYER` is checked if already set to avoid deactivating the target layer unless needed*. + +```c +// in keymap.c +layer_state_t default_layer_state_set_user(layer_state_t state) { + // switch on change in default layer need to check if target layer already set to avoid turning off layer needlessly + switch(get_highest_layer(state)) { + case _DEFAULT_LAYER_2: + if ((AUTO_MOUSE_TARGET_LAYER) == _MOUSE_LAYER_2) break; + auto_mouse_layer_off(); + set_auto_mouse_layer(_MOUSE_LAYER_2); + break; + + default: + if((AUTO_MOUSE_TARGET_LAYER) == _MOUSE_LAYER_1) break; + auto_mouse_layer_off(); + set_auto_mouse_layer(_MOUSE_LAYER_1); + } + return state; +} +``` + +### Use custom keys to control auto mouse: +Custom key records could also be created that control the auto mouse feature. +The code example below would create a custom key that would toggle the auto mouse feature on and off when pressed while also setting a bool that could be used to disable other code that may turn it on such as the layer code above. + +```c +// in config.h: +enum user_custom_keycodes { + AM_Toggle = SAFE_RANGE +}; + +// in keymap.c: +// set up global bool to adjust other user code +bool auto_mouse_tg_off = !AUTO_MOUSE_ENABLED; + +bool process_record_user(uint16_t keycode, keyrecord_t* record) { + switch (keycode) { + // toggle auto mouse enable key + case AM_Toggle: + if(record->event.pressed) { // key down + auto_mouse_layer_off(); // disable target layer if needed + set_auto_mouse_enabled((AUTO_MOUSE_ENABLED) ^ 1); + auto_mouse_tg_off = !get_auto_mouse_enabled(); + } // do nothing on key up + return false; // prevent further processing of keycode + } +} +``` + + +## Customize Target Layer Activation + +Layer activation can be customized by overwriting the `auto_mouse_activation` function. This function is checked every time `pointing_device_task` is called when inactive and every `AUTO_MOUSE_DEBOUNCE` ms when active, and will evaluate pointing device level conditions that trigger target layer activation. When it returns true, the target layer will be activated barring the usual exceptions _(e.g. delay time has not expired)_. + +By default it will return true if any of the `mouse_report` axes `x`,`y`,`h`,`v` are non zero, or if there is any mouse buttons active in `mouse_report`. +_Note: The Cirque pinnacle track pad already implements a custom activation function that will activate on touchdown as well as movement all of the default conditions, currently this only works for the master side of split keyboards._ + +| Function | Description | Return type | +| :--------------------------------------------------------- | -------------------------------------------------------------------------------- | --------------: | +| `auto_mouse_activation(report_mouse_t mouse_report)` | Overwritable function that controls target layer activation (when true) | `bool` | + +## Auto Mouse for Custom Pointing Device Task + +When using a custom pointing device (overwriting `pointing_device_task`) the following code should be somewhere in the `pointing_device_task_*` stack: + +```c +bool pointing_device_task(void) { + //...Custom pointing device task code + + // handle automatic mouse layer (needs report_mouse_t as input) + pointing_device_task_auto_mouse(local_mouse_report); + + //...More custom pointing device task code + + return pointing_device_send(); +} +``` + +In general the following two functions must be implemented in appropriate locations for auto mouse to function: + +| Function | Description | Suggested location | +| -------------------------------------------------------------- | ------------------------------------------------------------ | ---------------------------: | +| `pointing_device_task_auto_mouse(report_mouse_t mouse_report)` | handles target layer activation and is_active status updates | `pointing_device_task` stack | +| `process_auto_mouse(uint16_t keycode, keyrecord_t* record)` | Keycode processing for auto mouse | `process_record` stack | diff --git a/docs/features/programmable_button.md b/docs/features/programmable_button.md new file mode 100644 index 0000000000..1fdf44c29e --- /dev/null +++ b/docs/features/programmable_button.md @@ -0,0 +1,146 @@ +# Programmable Button {#programmable-button} + +Programmable Buttons are keys that have no predefined meaning. This means they can be processed on the host side by custom software without the operating system trying to interpret them. + +The keycodes are emitted according to the HID Telephony Device page (`0x0B`), Programmable Button usage (`0x09`). On Linux (> 5.14) they are handled automatically and translated to `KEY_MACRO#` keycodes (up to `KEY_MACRO30`). + +::: tip +Currently there is no known support in Windows or macOS. It may be possible to write a custom HID driver to receive these usages, but this is out of the scope of the QMK documentation. +::: + +## Usage {#usage} + +Add the following to your `rules.mk`: + +```make +PROGRAMMABLE_BUTTON_ENABLE = yes +``` + +## Keycodes {#keycodes} + +|Key |Aliases|Description | +|---------------------------|-------|----------------------| +|`QK_PROGRAMMABLE_BUTTON_1` |`PB_1` |Programmable button 1 | +|`QK_PROGRAMMABLE_BUTTON_2` |`PB_2` |Programmable button 2 | +|`QK_PROGRAMMABLE_BUTTON_3` |`PB_3` |Programmable button 3 | +|`QK_PROGRAMMABLE_BUTTON_4` |`PB_4` |Programmable button 4 | +|`QK_PROGRAMMABLE_BUTTON_5` |`PB_5` |Programmable button 5 | +|`QK_PROGRAMMABLE_BUTTON_6` |`PB_6` |Programmable button 6 | +|`QK_PROGRAMMABLE_BUTTON_7` |`PB_7` |Programmable button 7 | +|`QK_PROGRAMMABLE_BUTTON_8` |`PB_8` |Programmable button 8 | +|`QK_PROGRAMMABLE_BUTTON_9` |`PB_9` |Programmable button 9 | +|`QK_PROGRAMMABLE_BUTTON_10`|`PB_10`|Programmable button 10| +|`QK_PROGRAMMABLE_BUTTON_11`|`PB_11`|Programmable button 11| +|`QK_PROGRAMMABLE_BUTTON_12`|`PB_12`|Programmable button 12| +|`QK_PROGRAMMABLE_BUTTON_13`|`PB_13`|Programmable button 13| +|`QK_PROGRAMMABLE_BUTTON_14`|`PB_14`|Programmable button 14| +|`QK_PROGRAMMABLE_BUTTON_15`|`PB_15`|Programmable button 15| +|`QK_PROGRAMMABLE_BUTTON_16`|`PB_16`|Programmable button 16| +|`QK_PROGRAMMABLE_BUTTON_17`|`PB_17`|Programmable button 17| +|`QK_PROGRAMMABLE_BUTTON_18`|`PB_18`|Programmable button 18| +|`QK_PROGRAMMABLE_BUTTON_19`|`PB_19`|Programmable button 19| +|`QK_PROGRAMMABLE_BUTTON_20`|`PB_20`|Programmable button 20| +|`QK_PROGRAMMABLE_BUTTON_21`|`PB_21`|Programmable button 21| +|`QK_PROGRAMMABLE_BUTTON_22`|`PB_22`|Programmable button 22| +|`QK_PROGRAMMABLE_BUTTON_23`|`PB_23`|Programmable button 23| +|`QK_PROGRAMMABLE_BUTTON_24`|`PB_24`|Programmable button 24| +|`QK_PROGRAMMABLE_BUTTON_25`|`PB_25`|Programmable button 25| +|`QK_PROGRAMMABLE_BUTTON_26`|`PB_26`|Programmable button 26| +|`QK_PROGRAMMABLE_BUTTON_27`|`PB_27`|Programmable button 27| +|`QK_PROGRAMMABLE_BUTTON_28`|`PB_28`|Programmable button 28| +|`QK_PROGRAMMABLE_BUTTON_29`|`PB_29`|Programmable button 29| +|`QK_PROGRAMMABLE_BUTTON_30`|`PB_30`|Programmable button 30| +|`QK_PROGRAMMABLE_BUTTON_31`|`PB_31`|Programmable button 31| +|`QK_PROGRAMMABLE_BUTTON_32`|`PB_32`|Programmable button 32| + +## API {#api} + +### `void programmable_button_clear(void)` {#api-programmable-button-clear} + +Clear the programmable button report. + +--- + +### `void programmable_button_add(uint8_t index)` {#api-programmable-button-add} + +Set the state of a button. + +#### Arguments {#api-programmable-button-add-arguments} + + - `uint8_t index` + The index of the button to press, from 0 to 31. + +--- + +### `void programmable_button_remove(uint8_t index)` {#api-programmable-button-remove} + +Reset the state of a button. + +#### Arguments {#api-programmable-button-remove-arguments} + + - `uint8_t index` + The index of the button to release, from 0 to 31. + +--- + +### `void programmable_button_register(uint8_t index)` {#api-programmable-button-register} + +Set the state of a button, and flush the report. + +#### Arguments {#api-programmable-button-register-arguments} + + - `uint8_t index` + The index of the button to press, from 0 to 31. + +--- + +### `void programmable_button_unregister(uint8_t index)` {#api-programmable-button-unregister} + +Reset the state of a button, and flush the report. + +#### Arguments {#api-programmable-button-unregister-arguments} + + - `uint8_t index` + The index of the button to release, from 0 to 31. + +--- + +### `bool programmable_button_is_on(uint8_t index)` {#api-programmable-button-is-on} + +Get the state of a button. + +#### Arguments {#api-programmable-button-is-on-arguments} + + - `uint8_t index` + The index of the button to check, from 0 to 31. + +#### Return Value {#api-programmable-button-is-on-return} + +`true` if the button is pressed. + +--- + +### `void programmable_button_flush(void)` {#api-programmable-button-flush} + +Send the programmable button report to the host. + +--- + +### `uint32_t programmable_button_get_report(void)` {#api-programmable-button-get-report} + +Get the programmable button report. + +#### Return Value {#api-programmable-button-get-report-return} + +The bitmask of programmable button states. + +--- + +### `void programmable_button_set_report(uint32_t report)` {#api-programmable-button-set-report} + +Set the programmable button report. + +#### Arguments {#api-programmable-button-set-report-arguments} + + - `uint32_t report` + A bitmask of programmable button states. diff --git a/docs/features/ps2_mouse.md b/docs/features/ps2_mouse.md new file mode 100644 index 0000000000..90f4cca827 --- /dev/null +++ b/docs/features/ps2_mouse.md @@ -0,0 +1,349 @@ +# PS/2 Mouse Support {#ps2-mouse-support} + +Its possible to hook up a PS/2 mouse (for example touchpads or trackpoints) to your keyboard as a composite device. + +To hook up a Trackpoint, you need to obtain a Trackpoint module (i.e. harvest from a Thinkpad keyboard), identify the function of each pin of the module, and make the necessary circuitry between controller and Trackpoint module. For more information, please refer to [Trackpoint Hardware](https://deskthority.net/wiki/TrackPoint_Hardware) page on Deskthority Wiki. + +There are three available modes for hooking up PS/2 devices: USART (best), interrupts (better) or busywait (not recommended). + +## The Circuitry between Trackpoint and Controller {#the-circuitry-between-trackpoint-and-controller} + +To get the things working, a 4.7K drag is needed between the two lines DATA and CLK and the line 5+. + +``` + + DATA ----------+--------- PIN + | + 4.7K + | +MODULE 5+ --------+--+--------- PWR CONTROLLER + | + 4.7K + | + CLK ------+------------ PIN +``` + + +## Busywait Version {#busywait-version} + +Note: This is not recommended, you may encounter jerky movement or unsent inputs. Please use interrupt or USART version if possible. + +In rules.mk: + +```make +PS2_MOUSE_ENABLE = yes +PS2_ENABLE = yes +PS2_DRIVER = busywait +``` + +In your keyboard config.h: + +```c +#ifdef PS2_DRIVER_BUSYWAIT +# define PS2_CLOCK_PIN D1 +# define PS2_DATA_PIN D2 +#endif +``` + +### Interrupt Version (AVR/ATMega32u4) {#interrupt-version-avr} + +The following example uses D2 for clock and D5 for data. You can use any INT or PCINT pin for clock, and any pin for data. + +In rules.mk: + +```make +PS2_MOUSE_ENABLE = yes +PS2_ENABLE = yes +PS2_DRIVER = interrupt +``` + +In your keyboard config.h: + +```c +#ifdef PS2_DRIVER_INTERRUPT +#define PS2_CLOCK_PIN D2 +#define PS2_DATA_PIN D5 + +#define PS2_INT_INIT() do { \ + EICRA |= ((1<Z key types another "`z`." This is useful for +typing doubled letters, like the `z` in "`dazzle`": a double tap on Z +can instead be a roll from Z to Repeat, which is +potentially faster and more comfortable. The Repeat Key is also useful for +hotkeys, like repeating Ctrl + Shift + Right Arrow to select by word. + +Repeat Key remembers mods that were active with the last key press. These mods +are combined with any additional mods while pressing the Repeat Key. If the last +press key was Ctrl + Z, then Shift + +Repeat performs Ctrl + Shift + `Z`. + +## How do I enable Repeat Key + +In your `rules.mk`, add: + +```make +REPEAT_KEY_ENABLE = yes +``` + +Then pick a key in your keymap and assign it the keycode `QK_REPEAT_KEY` (short +alias `QK_REP`). Optionally, use the keycode `QK_ALT_REPEAT_KEY` (short alias +`QK_AREP`) on another key. + +## Keycodes + +|Keycode |Aliases |Description | +|-----------------------|---------|-------------------------------------| +|`QK_REPEAT_KEY` |`QK_REP` |Repeat the last pressed key | +|`QK_ALT_REPEAT_KEY` |`QK_AREP`|Perform alternate of the last key | + +## Alternate Repeating + +The Alternate Repeat Key performs the "alternate" action of the last pressed key +if it is defined. By default, Alternate Repeat is defined for navigation keys to +act in the reverse direction. When the last key is the common "select by word" +hotkey Ctrl + Shift + Right Arrow, the Alternate Repeat Key performs Ctrl + +Shift + Left Arrow, which together with the Repeat Key enables convenient +selection by words in either direction. + +Alternate Repeat is enabled with the Repeat Key by default. Optionally, to +reduce firmware size, Alternate Repeat may be disabled by adding in config.h: + +```c +#define NO_ALT_REPEAT_KEY +``` + +The following alternate keys are defined by default. See +`get_alt_repeat_key_keycode_user()` below for how to change or add to these +definitions. Where it makes sense, these definitions also include combinations +with mods, like Ctrl + Left ↔ Ctrl + Right Arrow. + +**Navigation** + +|Keycodes |Description | +|-----------------------------------|-----------------------------------| +|`KC_LEFT` ↔ `KC_RGHT` | Left ↔ Right Arrow | +|`KC_UP` ↔ `KC_DOWN` | Up ↔ Down Arrow | +|`KC_HOME` ↔ `KC_END` | Home ↔ End | +|`KC_PGUP` ↔ `KC_PGDN` | Page Up ↔ Page Down | +|`KC_MS_L` ↔ `KC_MS_R` | Mouse Cursor Left ↔ Right | +|`KC_MS_U` ↔ `KC_MS_D` | Mouse Cursor Up ↔ Down | +|`KC_WH_L` ↔ `KC_WH_R` | Mouse Wheel Left ↔ Right | +|`KC_WH_U` ↔ `KC_WH_D` | Mouse Wheel Up ↔ Down | + +**Misc** + +|Keycodes |Description | +|-----------------------------------|-----------------------------------| +|`KC_BSPC` ↔ `KC_DEL` | Backspace ↔ Delete | +|`KC_LBRC` ↔ `KC_RBRC` | `[` ↔ `]` | +|`KC_LCBR` ↔ `KC_RCBR` | `{` ↔ `}` | + +**Media** + +|Keycodes |Description | +|-----------------------------------|-----------------------------------| +|`KC_WBAK` ↔ `KC_WFWD` | Browser Back ↔ Forward | +|`KC_MNXT` ↔ `KC_MPRV` | Next ↔ Previous Media Track | +|`KC_MFFD` ↔ `KC_MRWD` | Fast Forward ↔ Rewind Media | +|`KC_VOLU` ↔ `KC_VOLD` | Volume Up ↔ Down | +|`KC_BRIU` ↔ `KC_BRID` | Brightness Up ↔ Down | + +**Hotkeys in Vim, Emacs, and other programs** + +|Keycodes |Description | +|-----------------------------------|-----------------------------------| +|mod + `KC_F` ↔ mod + `KC_B` | Forward ↔ Backward | +|mod + `KC_D` ↔ mod + `KC_U` | Down ↔ Up | +|mod + `KC_N` ↔ mod + `KC_P` | Next ↔ Previous | +|mod + `KC_A` ↔ mod + `KC_E` | Home ↔ End | +|mod + `KC_O` ↔ mod + `KC_I` | Vim jump list Older ↔ Newer | +|`KC_J` ↔ `KC_K` | Down ↔ Up | +|`KC_H` ↔ `KC_L` | Left ↔ Right | +|`KC_W` ↔ `KC_B` | Forward ↔ Backward by Word | + +(where above, "mod" is Ctrl, Alt, or GUI) + + +## Defining alternate keys + +Use the `get_alt_repeat_key_keycode_user()` callback to define the "alternate" +for additional keys or override the default definitions. For example, to define +Ctrl + Y as the alternate of Ctrl + Z, and vice versa, add the following in +keymap.c: + +```c +uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) { + if ((mods & MOD_MASK_CTRL)) { // Was Ctrl held? + switch (keycode) { + case KC_Y: return C(KC_Z); // Ctrl + Y reverses to Ctrl + Z. + case KC_Z: return C(KC_Y); // Ctrl + Z reverses to Ctrl + Y. + } + } + + return KC_TRNS; // Defer to default definitions. +} +``` + +The `keycode` and `mods` args are the keycode and mods that were active with the +last pressed key. The meaning of the return value from this function is: + +* `KC_NO` – do nothing (any predefined alternate key is not used); +* `KC_TRNS` – use the default alternate key if it exists; +* anything else – use the specified keycode. Any keycode may be returned + as an alternate key, including custom keycodes. + +Another example, defining Shift + Tab as the alternate of Tab, and vice versa: + +```c +uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) { + bool shifted = (mods & MOD_MASK_SHIFT); // Was Shift held? + switch (keycode) { + case KC_TAB: + if (shifted) { // If the last key was Shift + Tab, + return KC_TAB; // ... the reverse is Tab. + } else { // Otherwise, the last key was Tab, + return S(KC_TAB); // ... and the reverse is Shift + Tab. + } + } + + return KC_TRNS; +} +``` + +#### Eliminating SFBs + +Alternate Repeat can be configured more generally to perform an action that +"complements" the last key. Alternate Repeat is not limited to reverse +repeating, and it need not be symmetric. You can use it to eliminate cases of +same-finger bigrams in your layout, that is, pairs of letters typed by the same +finger. The following addresses the top 5 same-finger bigrams in English on +QWERTY, so that for instance "`ed`" may be typed as E, Alt +Repeat. + +```c +uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) { + switch (keycode) { + case KC_E: return KC_D; // For "ED" bigram. + case KC_D: return KC_E; // For "DE" bigram. + case KC_C: return KC_E; // For "CE" bigram. + case KC_L: return KC_O; // For "LO" bigram. + case KC_U: return KC_N; // For "UN" bigram. + } + + return KC_TRNS; +} +``` + +#### Typing shortcuts + +A useful possibility is having Alternate Repeat press [a +macro](../feature_macros). This way macros can be used without having to +dedicate keys to them. The following defines a couple shortcuts. + +* Typing K, Alt Repeat produces "`keyboard`," with the + initial "`k`" typed as usual and the "`eybord`" produced by the macro. +* Typing ., Alt Repeat produces "`../`," handy for "up + directory" on the shell. Similary, . types the initial "`.`" and + "`./`" is produced by the macro. + +```c +enum custom_keycodes { + M_KEYBOARD = SAFE_RANGE, + M_UPDIR, + // Other custom keys... +}; + +uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) { + switch (keycode) { + case KC_K: return M_KEYBOARD; + case KC_DOT: return M_UPDIR; + } + + return KC_TRNS; +} + +bool process_record_user(uint16_t keycode, keyrecord_t* record) { + switch (keycode) { + case M_KEYBOARD: SEND_STRING(/*k*/"eyboard"); break; + case M_UPDIR: SEND_STRING(/*.*/"./"); break; + } + return true; +} +``` + +## Ignoring certain keys and mods + +In tracking what is "the last key" to be repeated or alternate repeated, +modifier and layer switch keys are always ignored. This makes it possible to set +some mods and change layers between pressing a key and repeating it. By default, +all other (non-modifier, non-layer switch) keys are remembered so that they are +eligible for repeating. To configure additional keys to be ignored, define +`remember_last_key_user()` in your keymap.c. + +#### Ignoring a key + +The following ignores the Backspace key: + +```c +bool remember_last_key_user(uint16_t keycode, keyrecord_t* record, + uint8_t* remembered_mods) { + switch (keycode) { + case KC_BSPC: + return false; // Ignore backspace. + } + + return true; // Other keys can be repeated. +} +``` + +Then for instance, the Repeat key in Left Arrow, +Backspace, Repeat sends Left Arrow again instead of +repeating Backspace. + +The `remember_last_key_user()` callback is called on every key press excluding +modifiers and layer switches. Returning true indicates the key is remembered, +while false means it is ignored. + +#### Filtering remembered mods + +The `remembered_mods` arg represents the mods that will be remembered with +this key. It can be modified to forget certain mods. This may be +useful to forget capitalization when repeating shifted letters, so that "Aaron" +does not becom "AAron": + +```c +bool remember_last_key_user(uint16_t keycode, keyrecord_t* record, + uint8_t* remembered_mods) { + // Forget Shift on letter keys when Shift or AltGr are the only mods. + switch (keycode) { + case KC_A ... KC_Z: + if ((*remembered_mods & ~(MOD_MASK_SHIFT | MOD_BIT(KC_RALT))) == 0) { + *remembered_mods &= ~MOD_MASK_SHIFT; + } + break; + } + + return true; +} +``` + +#### Further conditions + +Besides checking the keycode, this callback could also make conditions based on +the current layer state (with `IS_LAYER_ON(layer)`) or mods (`get_mods()`). For +example, the following ignores keys on layer 2 as well as key combinations +involving GUI: + +```c +bool remember_last_key_user(uint16_t keycode, keyrecord_t* record, + uint8_t* remembered_mods) { + if (IS_LAYER_ON(2) || (get_mods() & MOD_MASK_GUI)) { + return false; // Ignore layer 2 keys and GUI chords. + } + + return true; // Other keys can be repeated. +} +``` + +::: tip +See [Layer Functions](../feature_layers#functions) and [Checking Modifier State](../feature_advanced_keycodes#checking-modifier-state) for further details. +::: + +## Handle how a key is repeated + +By default, pressing the Repeat Key will simply behave as if the last key +were pressed again. This also works with macro keys with custom handlers, +invoking the macro again. In case fine-tuning is needed for sensible repetition, +you can handle how a key is repeated with `get_repeat_key_count()` within +`process_record_user()`. + +The `get_repeat_key_count()` function returns a signed count of times the key +has been repeated or alternate repeated. When a key is pressed as usual, +`get_repeat_key_count()` is 0. On the first repeat, it is 1, then the second +repeat, 2, and so on. Negative counts are used similarly for alternate +repeating. For instance supposing `MY_MACRO` is a custom keycode used in the +layout: + +```c +bool process_record_user(uint16_t keycode, keyrecord_t* record) { + switch (keycode) { + case MY_MACRO: + if (get_repeat_key_count() > 0) { + // MY_MACRO is being repeated! + if (record->event.pressed) { + SEND_STRING("repeat!"); + } + } else { + // MY_MACRO is being used normally. + if (record->event.pressed) { + SEND_STRING("macro"); + } + } + return false; + + // Other macros... + } + return true; +} +``` + +## Handle how a key is alternate repeated + +Pressing the Alternate Repeat Key behaves as if the "alternate" of the last +pressed key were pressed, if an alternate is defined. To define how a particular +key is alternate repeated, use the `get_alt_repeat_key_keycode_user()` callback +as described above to define which keycode to use as its alternate. Beyond this, +`get_repeat_key_count()` may be used in custom handlers to fine-tune behavior +when alternate repeating. + +The following example defines `MY_MACRO` as its own alternate, and specially +handles repeating and alternate repeating: + +```c +uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) { + switch (keycode) { + case MY_MACRO: return MY_MACRO; // MY_MACRO is its own alternate. + } + return KC_TRNS; +} + +bool process_record_user(uint16_t keycode, keyrecord_t* record) { + switch (keycode) { + case MY_MACRO: + if (get_repeat_key_count() > 0) { // Repeating. + if (record->event.pressed) { + SEND_STRING("repeat!"); + } + } else if (get_repeat_key_count() < 0) { // Alternate repeating. + if (record->event.pressed) { + SEND_STRING("alt repeat!"); + } + } else { // Used normally. + if (record->event.pressed) { + SEND_STRING("macro"); + } + } + return false; + + // Other macros... + } + return true; +} +``` + + +## Functions + +| Function | Description | +|--------------------------------|------------------------------------------------------------------------| +| `get_last_keycode()` | The last key's keycode, the key to be repeated. | +| `get_last_mods()` | Mods to apply when repeating. | +| `set_last_keycode(kc)` | Set the keycode to be repeated. | +| `set_last_mods(mods)` | Set the mods to apply when repeating. | +| `get_repeat_key_count()` | Signed count of times the key has been repeated or alternate repeated. | +| `get_alt_repeat_key_keycode()` | Keycode to be used for alternate repeating. | + + +## Additional "Alternate" keys + +By leveraging `get_last_keycode()` in macros, it is possible to define +additional, distinct "Alternate Repeat"-like keys. The following defines two +keys `ALTREP2` and `ALTREP3` and implements ten shortcuts with them for common +English 5-gram letter patterns, taking inspiration from +[Stenotype](stenography): + + +| Typing | Produces | Typing | Produces | +|----------------------------------|----------|----------------------------------|----------| +| A, ALTREP2 | `ation` | A, ALTREP3 | `about` | +| I, ALTREP2 | `ition` | I, ALTREP3 | `inter` | +| S, ALTREP2 | `ssion` | S, ALTREP3 | `state` | +| T, ALTREP2 | `their` | T, ALTREP3 | `there` | +| W, ALTREP2 | `which` | W, ALTREP3 | `would` | + +```c +enum custom_keycodes { + ALTREP2 = SAFE_RANGE, + ALTREP3, +}; + +// Use ALTREP2 and ALTREP3 in your layout... + +bool remember_last_key_user(uint16_t keycode, keyrecord_t* record, + uint8_t* remembered_mods) { + switch (keycode) { + case ALTREP2: + case ALTREP3: + return false; // Ignore ALTREP keys. + } + + return true; // Other keys can be repeated. +} + +static void process_altrep2(uint16_t keycode, uint8_t mods) { + switch (keycode) { + case KC_A: SEND_STRING(/*a*/"tion"); break; + case KC_I: SEND_STRING(/*i*/"tion"); break; + case KC_S: SEND_STRING(/*s*/"sion"); break; + case KC_T: SEND_STRING(/*t*/"heir"); break; + case KC_W: SEND_STRING(/*w*/"hich"); break; + } +} + +static void process_altrep3(uint16_t keycode, uint8_t mods) { + switch (keycode) { + case KC_A: SEND_STRING(/*a*/"bout"); break; + case KC_I: SEND_STRING(/*i*/"nter"); break; + case KC_S: SEND_STRING(/*s*/"tate"); break; + case KC_T: SEND_STRING(/*t*/"here"); break; + case KC_W: SEND_STRING(/*w*/"ould"); break; + } +} + +bool process_record_user(uint16_t keycode, keyrecord_t* record) { + switch (keycode) { + case ALTREP2: + if (record->event.pressed) { + process_altrep2(get_last_keycode(), get_last_mods()); + } + return false; + + case ALTREP3: + if (record->event.pressed) { + process_altrep3(get_last_keycode(), get_last_mods()); + } + return false; + } + + return true; +} +``` + diff --git a/docs/features/rgb_matrix.md b/docs/features/rgb_matrix.md new file mode 100644 index 0000000000..f7f693f239 --- /dev/null +++ b/docs/features/rgb_matrix.md @@ -0,0 +1,1119 @@ +# RGB Matrix Lighting {#rgb-matrix-lighting} + +This feature allows you to use RGB LED matrices driven by external drivers. It hooks into the RGBLIGHT system so you can use the same keycodes as RGBLIGHT to control it. + +If you want to use single color LED's you should use the [LED Matrix Subsystem](led_matrix) instead. + +## Driver configuration {#driver-configuration} +--- +### IS31FL3731 {#is31fl3731} + +There is basic support for addressable RGB matrix lighting with the I2C IS31FL3731 RGB controller. To enable it, add this to your `rules.mk`: + +```make +RGB_MATRIX_ENABLE = yes +RGB_MATRIX_DRIVER = is31fl3731 +``` + +You can use between 1 and 4 IS31FL3731 IC's. Do not specify `DRIVER_ADDR_` defines for IC's that are not present on your keyboard. You can define the following items in `config.h`: + +| Variable | Description | Default | +|----------|-------------|---------| +| `IS31FL3731_I2C_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 | +| `IS31FL3731_I2C_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 | +| `IS31FL3731_DEGHOST` | (Optional) Set this define to enable de-ghosting by halving Vcc during blanking time | | +| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | | +| `IS31FL3731_I2C_ADDRESS_1` | (Required) Address for the first RGB driver | | +| `IS31FL3731_I2C_ADDRESS_2` | (Optional) Address for the second RGB driver | | +| `IS31FL3731_I2C_ADDRESS_3` | (Optional) Address for the third RGB driver | | +| `IS31FL3731_I2C_ADDRESS_4` | (Optional) Address for the fourth RGB driver | | + +Here is an example using 2 drivers. + +```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) +// The address will vary depending on your wiring: +// 00 AD <-> GND +// 01 AD <-> SCL +// 10 AD <-> SDA +// 11 AD <-> VCC +// ADDR represents A1:A0 of the 7-bit address. +// The result is: 0b11101(ADDR) +#define IS31FL3731_I2C_ADDRESS_1 IS31FL3731_I2C_ADDRESS_GND +#define IS31FL3731_I2C_ADDRESS_2 IS31FL3731_I2C_ADDRESS_SDA + +#define DRIVER_1_LED_TOTAL 25 +#define DRIVER_2_LED_TOTAL 24 +#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL) +``` + +::: warning +Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`. +::: + +For split keyboards using `RGB_MATRIX_SPLIT` with an LED driver, you can either have the same driver address or different driver addresses. If using different addresses, use `IS31FL3731_I2C_ADDRESS_1` for one and `IS31FL3731_I2C_ADDRESS_2` for the other one. Then, in `g_is31fl3731_leds`, fill out the correct driver index (0 or 1). If using one address, use `IS31FL3731_I2C_ADDRESS_1` for both, and use index 0 for `g_is31fl3731_leds`. + +Define these arrays listing all the LEDs in your `.c`: + +```c +const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT] = { +/* Refer to IS31 manual for these locations + * driver + * | R location + * | | G location + * | | | B location + * | | | | */ + {0, C1_3, C2_3, C3_3}, + .... +} +``` + +Where `Cx_y` is the location of the LED in the matrix defined by [the datasheet](https://www.issi.com/WW/pdf/31FL3731.pdf) and the header file `drivers/led/issi/is31fl3731.h`. The `driver` is the index of the driver you defined in your `config.h` (`0`, `1`, `2`, or `3`). + +--- +### IS31FL3733 {#is31fl3733} + +There is basic support for addressable RGB matrix lighting with the I2C IS31FL3733 RGB controller. To enable it, add this to your `rules.mk`: + +```make +RGB_MATRIX_ENABLE = yes +RGB_MATRIX_DRIVER = is31fl3733 +``` + +You can use between 1 and 4 IS31FL3733 IC's. Do not specify `DRIVER_ADDR_` defines for IC's that are not present on your keyboard. You can define the following items in `config.h`: + +| Variable | Description | Default | +|----------|-------------|---------| +| `IS31FL3733_I2C_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 | +| `IS31FL3733_I2C_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 | +| `IS31FL3733_PWM_FREQUENCY` | (Optional) PWM Frequency Setting - IS31FL3733B only | 0 | +| `IS31FL3733_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF | +| `IS31FL3733_SWPULLUP` | (Optional) Set the value of the SWx lines on-chip de-ghosting resistors | PUR_0R (Disabled) | +| `IS31FL3733_CSPULLUP` | (Optional) Set the value of the CSx lines on-chip de-ghosting resistors | PUR_0R (Disabled) | +| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | | +| `IS31FL3733_I2C_ADDRESS_1` | (Required) Address for the first RGB driver | | +| `IS31FL3733_I2C_ADDRESS_2` | (Optional) Address for the second RGB driver | | +| `IS31FL3733_I2C_ADDRESS_3` | (Optional) Address for the third RGB driver | | +| `IS31FL3733_I2C_ADDRESS_4` | (Optional) Address for the fourth RGB driver | | +| `IS31FL3733_SYNC_1` | (Optional) Sync configuration for the first RGB driver | 0 | +| `IS31FL3733_SYNC_2` | (Optional) Sync configuration for the second RGB driver | 0 | +| `IS31FL3733_SYNC_3` | (Optional) Sync configuration for the third RGB driver | 0 | +| `IS31FL3733_SYNC_4` | (Optional) Sync configuration for the fourth RGB driver | 0 | + +The IS31FL3733 IC's have on-chip resistors that can be enabled to allow for de-ghosting of the RGB matrix. By default these resistors are not enabled (`IS31FL3733_SWPULLUP`/`IS31FL3733_CSPULLUP` are given the value of `IS31FL3733_PUR_0R`), the values that can be set to enable de-ghosting are as follows: + +| `IS31FL3733_SWPULLUP/IS31FL3733_CSPULLUP` | Description | +|----------------------|-------------| +| `IS31FL3733_PUR_0R` | (default) Do not use the on-chip resistors/enable de-ghosting | +| `IS31FL3733_PUR_05KR` | The 0.5k Ohm resistor used during blanking period (t_NOL) | +| `IS31FL3733_PUR_3KR` | The 3k Ohm resistor used at all times | +| `IS31FL3733_PUR_4KR` | The 4k Ohm resistor used at all times | +| `IS31FL3733_PUR_8KR` | The 8k Ohm resistor used at all times | +| `IS31FL3733_PUR_16KR` | The 16k Ohm resistor used at all times | +| `IS31FL3733_PUR_32KR` | The 32k Ohm resistor used during blanking period (t_NOL) | + +Here is an example using 2 drivers. + +```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) +// The address will vary depending on your wiring: +// 00 ADDRn <-> GND +// 01 ADDRn <-> SCL +// 10 ADDRn <-> SDA +// 11 ADDRn <-> VCC +// ADDR1 represents A1:A0 of the 7-bit address. +// ADDR2 represents A3:A2 of the 7-bit address. +// The result is: 0b101(ADDR2)(ADDR1) +#define IS31FL3733_I2C_ADDRESS_1 IS31FL3733_I2C_ADDRESS_GND_GND +#define IS31FL3733_I2C_ADDRESS_2 IS31FL3733_I2C_ADDRESS_GND_VCC + +#define DRIVER_1_LED_TOTAL 58 +#define DRIVER_2_LED_TOTAL 10 +#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL) +``` + +::: warning +Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`. +::: + +Currently only 4 drivers are supported, but it would be trivial to support all 8 combinations. + +Define these arrays listing all the LEDs in your `.c`: + +```c +const is31fl3733_led_t PROGMEM g_is31fl3733_leds[IS31FL3733_LED_COUNT] = { +/* Refer to IS31 manual for these locations + * driver + * | R location + * | | G location + * | | | B location + * | | | | */ + {0, SW1_CS1, SW1_CS2, SW1_CS3}, + .... +} +``` + +Where `SWx_CSy` is the location of the LED in the matrix defined by [the datasheet](https://www.issi.com/WW/pdf/31FL3733.pdf) and the header file `drivers/led/issi/is31fl3733.h`. The `driver` is the index of the driver you defined in your `config.h` (`0`, `1`, `2`, or `3` for now). + +--- +### IS31FL3736 {#is31fl3736} + +There is basic support for addressable RGB matrix lighting with the I2C IS31FL3736 RGB controller. To enable it, add this to your `rules.mk`: + +```make +RGB_MATRIX_ENABLE = yes +RGB_MATRIX_DRIVER = is31fl3736 +``` +You can use between 1 and 4 IS31FL3736 IC's. Do not specify `DRIVER_ADDR_` defines for IC's that are not present on your keyboard. + +Configure the hardware via your `config.h`: + +| Variable | Description | Default | +|----------|-------------|---------| +| `IS31FL3736_I2C_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 | +| `IS31FL3736_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 | +| `IS31FL3736_PWM_FREQUENCY` | (Optional) PWM Frequency Setting - IS31FL3736B only | 0 | +| `IS31FL3736_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF | +| `IS31FL3736_SWPULLUP` | (Optional) Set the value of the SWx lines on-chip de-ghosting resistors | PUR_0R (Disabled) | +| `IS31FL3736_CSPULLUP` | (Optional) Set the value of the CSx lines on-chip de-ghosting resistors | PUR_0R (Disabled) | +| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | | +| `IS31FL3736_I2C_ADDRESS_1` | (Required) Address for the first RGB driver | | +| `IS31FL3736_I2C_ADDRESS_2` | (Optional) Address for the second RGB driver | | +| `IS31FL3736_I2C_ADDRESS_3` | (Optional) Address for the third RGB driver | | +| `IS31FL3736_I2C_ADDRESS_4` | (Optional) Address for the fourth RGB driver | | + +The IS31FL3736 IC's have on-chip resistors that can be enabled to allow for de-ghosting of the RGB matrix. By default these resistors are not enabled (`IS31FL3736_SWPULLUP`/`IS31FL3736_CSPULLUP` are given the value of `IS31FL3736_PUR_0R`), the values that can be set to enable de-ghosting are as follows: + +| `IS31FL3736_SWPULLUP/IS31FL3736_CSPULLUP` | Description | +|----------------------|-------------| +| `IS31FL3736_PUR_0R` | (default) Do not use the on-chip resistors/enable de-ghosting | +| `IS31FL3736_PUR_05KR` | The 0.5k Ohm resistor used during blanking period (t_NOL) | +| `IS31FL3736_PUR_1KR` | The 1k Ohm resistor used during blanking period (t_NOL) | +| `IS31FL3736_PUR_2KR` | The 2k Ohm resistor used during blanking period (t_NOL) | +| `IS31FL3736_PUR_4KR` | The 4k Ohm resistor used during blanking period (t_NOL) | +| `IS31FL3736_PUR_8KR` | The 8k Ohm resistor during blanking period (t_NOL) | +| `IS31FL3736_PUR_16KR` | The 16k Ohm resistor during blanking period (t_NOL) | +| `IS31FL3736_PUR_32KR` | The 32k Ohm resistor used during blanking period (t_NOL) | + +Here is an example using 2 drivers. + +```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) +// The address will vary depending on your wiring: +// 00 ADDRn <-> GND +// 01 ADDRn <-> SCL +// 10 ADDRn <-> SDA +// 11 ADDRn <-> VCC +// ADDR1 represents A1:A0 of the 7-bit address. +// ADDR2 represents A3:A2 of the 7-bit address. +// The result is: 0b101(ADDR2)(ADDR1) +#define IS31FL3736_I2C_ADDRESS_1 IS31FL3736_I2C_ADDRESS_GND_GND +#define IS31FL3736_I2C_ADDRESS_2 IS31FL3736_I2C_ADDRESS_GND_SCL + +#define DRIVER_1_LED_TOTAL 30 +#define DRIVER_2_LED_TOTAL 32 +#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL) +``` +::: warning +Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`. +::: + +Define these arrays listing all the LEDs in your `.c`: + +```c +const is31fl3736_led_t PROGMEM g_is31fl3736_leds[IS31FL3736_LED_COUNT] = { +/* Refer to IS31 manual for these locations + * driver + * | R location + * | | G location + * | | | B location + * | | | | */ + {0, SW1_CS1, SW1_CS2, SW1_CS3}, + .... +} +``` +### IS31FL3737 {#is31fl3737} + +There is basic support for addressable RGB matrix lighting with the I2C IS31FL3737 RGB controller. To enable it, add this to your `rules.mk`: + +```make +RGB_MATRIX_ENABLE = yes +RGB_MATRIX_DRIVER = is31fl3737 +``` +You can use between 1 and 4 IS31FL3737 IC's. Do not specify `DRIVER_ADDR_` defines for IC's that are not present on your keyboard. + +Configure the hardware via your `config.h`: + +| Variable | Description | Default | +|----------|-------------|---------| +| `IS31FL3737_I2C_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 | +| `IS31FL3737_I2C_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 | +| `IS31FL3737_PWM_FREQUENCY` | (Optional) PWM Frequency Setting - IS31FL3737B only | 0 | +| `IS31FL3737_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF | +| `IS31FL3737_SWPULLUP` | (Optional) Set the value of the SWx lines on-chip de-ghosting resistors | PUR_0R (Disabled) | +| `IS31FL3737_CSPULLUP` | (Optional) Set the value of the CSx lines on-chip de-ghosting resistors | PUR_0R (Disabled) | +| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | | +| `IS31FL3737_I2C_ADDRESS_1` | (Required) Address for the first RGB driver | | +| `IS31FL3737_I2C_ADDRESS_2` | (Optional) Address for the second RGB driver | | +| `IS31FL3737_I2C_ADDRESS_3` | (Optional) Address for the third RGB driver | | +| `IS31FL3737_I2C_ADDRESS_4` | (Optional) Address for the fourth RGB driver | | + +The IS31FL3737 IC's have on-chip resistors that can be enabled to allow for de-ghosting of the RGB matrix. By default these resistors are not enabled (`IS31FL3737_SWPULLUP`/`IS31FL3737_CSPULLUP` are given the value of `IS31FL3737_PUR_0R`), the values that can be set to enable de-ghosting are as follows: + +| `IS31FL3737_SWPULLUP/IS31FL3737_CSPULLUP` | Description | +|----------------------|-------------| +| `IS31FL3737_PUR_0R` | (default) Do not use the on-chip resistors/enable de-ghosting | +| `IS31FL3737_PUR_05KR` | The 0.5k Ohm resistor used during blanking period (t_NOL) | +| `IS31FL3737_PUR_1KR` | The 1k Ohm resistor used during blanking period (t_NOL) | +| `IS31FL3737_PUR_2KR` | The 2k Ohm resistor used during blanking period (t_NOL) | +| `IS31FL3737_PUR_4KR` | The 4k Ohm resistor used during blanking period (t_NOL) | +| `IS31FL3737_PUR_8KR` | The 8k Ohm resistor during blanking period (t_NOL) | +| `IS31FL3737_PUR_16KR` | The 16k Ohm resistor during blanking period (t_NOL) | +| `IS31FL3737_PUR_32KR` | The 32k Ohm resistor used during blanking period (t_NOL) | + +Here is an example using 2 drivers. + +```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) +// The address will vary depending on your wiring: +// 0000 ADDR <-> GND +// 0101 ADDR <-> SCL +// 1010 ADDR <-> SDA +// 1111 ADDR <-> VCC +// ADDR represents A3:A0 of the 7-bit address. +// The result is: 0b101(ADDR) +#define IS31FL3737_I2C_ADDRESS_1 IS31FL3737_I2C_ADDRESS_GND +#define IS31FL3737_I2C_ADDRESS_2 IS31FL3737_I2C_ADDRESS_SCL + +#define DRIVER_1_LED_TOTAL 30 +#define DRIVER_2_LED_TOTAL 36 +#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL) +``` +::: warning +Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`. +::: + +Define these arrays listing all the LEDs in your `.c`: + +```c +const is31fl3737_led_t PROGMEM g_is31fl3737_leds[IS31FL3737_LED_COUNT] = { +/* Refer to IS31 manual for these locations + * driver + * | R location + * | | G location + * | | | B location + * | | | | */ + {0, SW1_CS1, SW1_CS2, SW1_CS3}, + .... +} +``` + +Where `SWx_CSy` 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/led/issi/is31fl3737.h`. The `driver` is the index of the driver you defined in your `config.h` (Only `0`, `1`, `2`, or `3` for now). + +--- +### IS31FLCOMMON {#is31flcommon} + +There is basic support for addressable RGB matrix lighting with a selection of I2C ISSI Lumissil RGB controllers through a shared common driver. To enable it, add this to your `rules.mk`: + +```makefile +RGB_MATRIX_ENABLE = yes +RGB_MATRIX_DRIVER = +``` + +Where `` is the applicable LED driver chip as below + +| Driver Name | Data Sheet | Capability | +|-------------|------------|------------| +| `IS31FL3742A` | [datasheet](https://www.lumissil.com/assets/pdf/core/IS31FL3742A_DS.pdf) | 60 RGB, 30x6 Matrix | +| `IS31FL3743A` | [datasheet](https://www.lumissil.com/assets/pdf/core/IS31FL3743A_DS.pdf) | 66 RGB, 18x11 Matrix | +| `IS31FL3745` | [datasheet](https://www.lumissil.com/assets/pdf/core/IS31FL3745_DS.pdf) | 48 RGB, 18x8 Matrix | +| `IS31FL3746A` | [datasheet](https://www.lumissil.com/assets/pdf/core/IS31FL3746A_DS.pdf) | 24 RGB, 18x4 Matrix | + +You can use between 1 and 4 IC's. Do not specify `DRIVER_ADDR_` define for IC's if not present on your keyboard. The `DRIVER_ADDR_1` default assumes that all Address pins on the controller have been connected to GND. Drivers that have SYNC functionality have the default settings to disable if 1 driver. If more than 1 drivers then `DRIVER_ADDR_1` will be set to Master and the remaining ones set to Slave. + +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 | +| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | | +| `DRIVER_ADDR_1` | (Optional) Address for the first RGB driver | | +| `DRIVER_ADDR_` | (Required) Address for the additional RGB drivers | | +| `ISSI_SSR_` | (Optional) Configuration for the Spread Spectrum Register | | +| `ISSI_CONFIGURATION` | (Optional) Configuration for the Configuration Register | | +| `ISSI_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF | +| `ISSI_PULLDOWNUP` | (Optional) Configuration for the Pull Up & Pull Down Register | | +| `ISSI_TEMP` | (Optional) Configuration for the Temperature Register | | +| `ISSI_PWM_ENABLE` | (Optional) Configuration for the PWM Enable Register | | +| `ISSI_PWM_SET` | (Optional) Configuration for the PWM Setting Register | | +| `ISSI_SCAL_RED` | (Optional) Configuration for the RED LEDs in Scaling Registers | 0xFF | +| `ISSI_SCAL_BLUE` | (Optional) Configuration for the BLUE LEDs in Scaling Registers | 0xFF | +| `ISSI_SCAL_GREEN` | (Optional) Configuration for the GREEN LEDs in Scaling Registers | 0xFF | +| `ISSI_MANUAL_SCALING` | (Optional) If you wish to configure the Scaling Registers manually | | + + +Defaults + +| Variable | IS31FL3742A | IS31FL3743A | IS31FL3745 | IS31FL3746 | +|----------|-------------|-------------|------------|------------| +| `DRIVER_ADDR_1` | 0b0110000 | 0b0100000 | 0b0100000 | 0b1100000 | +| `ISSI_SSR_1` | 0x00 | 0x00 / 0x60 | 0x00 / 0xC0 | 0x00 | +| `ISSI_SSR_<2-4>` | 0x00 | 0x40 | 0x80 | 0x00 | +| `ISSI_CONFIGURATION` | 0x31 | 0x01 | 0x31 | 0x01 | +| `ISSI_PULLDOWNUP` | 0x55 | 0x33 | 0x33 | 0x33 | +| `ISSI_TEMP` | N/A | 0x00 | 0x00 | 0x00 | +| `ISSI_PWM_ENABLE` | N/A | N/A | N/A | 0x00 | +| `ISSI_PWM_SET` | 0x00 | N/A | N/A | 0x00 | + +Here is an example using 2 drivers. + +```c +#define DRIVER_ADDR_2 0b0100001 + +#define DRIVER_1_LED_TOTAL 66 +#define DRIVER_2_LED_TOTAL 42 +#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL) +``` + +::: warning +Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`. +::: + +Currently only 4 drivers are supported, but it would be trivial to support for more. Note that using a combination of different drivers is not supported. All drivers must be of the same model. + +Define these arrays listing all the LEDs in your `.c`: + +```c +const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT] = { +/* Refer to IS31 manual for these locations + * driver + * | R location + * | | G location + * | | | B location + * | | | | */ + {0, SW1_CS1, SW1_CS2, SW1_CS3}, + .... +} +``` + +Where `CSx_SWx` is the location of the LED in the matrix defined by the datasheet. The `driver` is the index of the driver you defined in your `config.h` (`0`, `1`, `2`, or `3` for now). + +`ISSI_MANUAL_SCALING` is used to override the Scaling for individual LED's. By default they will be set as per `ISSI_SCAL_`. In `config.h` set how many LED's you want to manually set scaling for. +Eg `#define ISSI_MANUAL_SCALING 3` + +Then Define the array listing all the LEDs you want to override in your `.c`: + +```c +const is31_led PROGMEM g_is31_scaling[ISSI_MANUAL_SCALING] = { +/* LED Index + * | R scaling + * | | G scaling + * | | | B scaling + * | | | | */ + {5, 120, 155, 167}, + {9, 120, 155, 167}, + .... +} +``` + +Where LED Index is the position of the LED in the `g_is31_leds` array. The `scaling` value between 0 and 255 to be written to the Scaling Register. + +--- + +### WS2812 {#ws2812} + +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`: + +```make +RGB_MATRIX_ENABLE = yes +RGB_MATRIX_DRIVER = ws2812 +``` + +Configure the hardware via your `config.h`: + +```c +// The pin connected to the data pin of the LEDs +#define WS2812_DI_PIN D7 +// The number of LEDs connected +#define RGB_MATRIX_LED_COUNT 70 +``` + +::: tip +There are additional configuration options for ARM controllers that offer increased performance over the default bitbang driver. Please see [WS2812 Driver](../drivers/ws2812) for more information. +::: + +--- + +### APA102 {#apa102} + +There is basic support for APA102 based addressable LED strands. To enable it, add this to your `rules.mk`: + +```make +RGB_MATRIX_ENABLE = yes +RGB_MATRIX_DRIVER = apa102 +``` + +Configure the hardware via your `config.h`: + +```c +// The pin connected to the data pin of the LEDs +#define APA102_DI_PIN D7 +// The pin connected to the clock pin of the LEDs +#define APA102_CI_PIN D6 +// The number of LEDs connected +#define RGB_MATRIX_LED_COUNT 70 +``` + +--- +### AW20216S {#aw20216s} +There is basic support for addressable RGB matrix lighting with the SPI AW20216S RGB controller. To enable it, add this to your `rules.mk`: + +```make +RGB_MATRIX_ENABLE = yes +RGB_MATRIX_DRIVER = aw20216s +``` + +You can use up to 2 AW20216S IC's. Do not specify `DRIVER__xxx` defines for IC's that are not present on your keyboard. You can define the following items in `config.h`: + +| Variable | Description | Default | +|----------|-------------|---------| +| `AW20216S_CS_PIN_1` | (Required) MCU pin connected to first RGB driver chip select line | B13 | +| `AW20216S_CS_PIN_2` | (Optional) MCU pin connected to second RGB driver chip select line | | +| `AW20216S_EN_PIN_1` | (Required) MCU pin connected to first RGB driver hardware enable line | C13 | +| `AW20216S_EN_PIN_2` | (Optional) MCU pin connected to second RGB driver hardware enable line | | +| `DRIVER_1_LED_TOTAL` | (Required) How many RGB lights are connected to first RGB driver | | +| `DRIVER_2_LED_TOTAL` | (Optional) How many RGB lights are connected to second RGB driver | | +| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | | +| `AW20216S_SCALING_MAX` | (Optional) LED current scaling value (0-255, higher values mean LED is brighter at full PWM) | 150 | +| `AW20216S_GLOBAL_CURRENT_MAX` | (Optional) Driver global current limit (0-255, higher values means the driver may consume more power) | 150 | +| `AW20216S_SPI_MODE` | (Optional) Mode for SPI communication (0-3, defines polarity and phase of the clock) | 3 | +| `AW20216S_SPI_DIVISOR` | (Optional) Clock divisor for SPI communication (powers of 2, smaller numbers means faster communication, should not be less than 4) | 4 | + +Here is an example using 2 drivers. + +```c +#define AW20216S_CS_PIN_1 B13 +#define AW20216S_CS_PIN_2 B14 +// Hardware enable lines may be connected to the same pin +#define AW20216S_EN_PIN_1 C13 +#define AW20216S_EN_PIN_2 C13 + +#define DRIVER_1_LED_TOTAL 66 +#define DRIVER_2_LED_TOTAL 32 +#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL) +``` + +::: warning +Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`. +::: + +Define these arrays listing all the LEDs in your `.c`: + +```c +const aw20216s_led_t PROGMEM g_aw20216s_leds[AW20216S_LED_COUNT] = { +/* Each AW20216S channel is controlled by a register at some offset between 0x00 + * and 0xD7 inclusive. + * See drivers/led/aw20216s.h for the mapping between register offsets and + * driver pin locations. + * driver + * | R location + * | | G location + * | | | B location + * | | | | */ + { 0, SW1_CS1, SW1_CS2, SW1_CS3 }, + { 0, SW1_CS4, SW1_CS5, SW1_CS6 }, + { 0, SW1_CS7, SW1_CS8, SW1_CS9 }, + { 0, SW1_CS10, SW1_CS11, SW1_CS12 }, + { 0, SW1_CS13, SW1_CS14, SW1_CS15 }, + ... + { 1, SW1_CS1, SW1_CS2, SW1_CS3 }, + { 1, SW1_CS13, SW1_CS14, SW1_CS15 }, + { 1, SW1_CS16, SW1_CS17, SW1_CS18 }, + { 1, SW2_CS4, SW2_CS5, SW2_CS6 }, + ... +}; +``` + +--- + +## Common Configuration {#common-configuration} + +From this point forward the configuration is the same for all the drivers. The `led_config_t` struct provides a key electrical matrix to led index lookup table, what the physical position of each LED is on the board, and what type of key or usage the LED if the LED represents. Here is a brief example: + +```c +led_config_t g_led_config = { { + // Key Matrix to LED Index + { 5, NO_LED, NO_LED, 0 }, + { NO_LED, NO_LED, NO_LED, NO_LED }, + { 4, NO_LED, NO_LED, 1 }, + { 3, NO_LED, NO_LED, 2 } +}, { + // LED Index to Physical Position + { 188, 16 }, { 187, 48 }, { 149, 64 }, { 112, 64 }, { 37, 48 }, { 38, 16 } +}, { + // LED Index to Flag + 1, 4, 4, 4, 4, 1 +} }; +``` + +The first part, `// Key Matrix to LED Index`, tells the system what key this LED represents by using the key's electrical matrix row & col. The second part, `// LED Index to Physical Position` represents the LED's physical `{ x, y }` position on the keyboard. The default expected range of values for `{ x, y }` is the inclusive range `{ 0..224, 0..64 }`. This default expected range is due to effects that calculate the center of the keyboard for their animations. The easiest way to calculate these positions is imagine your keyboard is a grid, and the top left of the keyboard represents `{ x, y }` coordinate `{ 0, 0 }` and the bottom right of your keyboard represents `{ 224, 64 }`. Using this as a basis, you can use the following formula to calculate the physical position: + +```c +x = 224 / (NUMBER_OF_COLS - 1) * COL_POSITION +y = 64 / (NUMBER_OF_ROWS - 1) * ROW_POSITION +``` + +Where NUMBER_OF_COLS, NUMBER_OF_ROWS, COL_POSITION, & ROW_POSITION are all based on the physical layout of your keyboard, not the electrical layout. + +As mentioned earlier, the center of the keyboard by default is expected to be `{ 112, 32 }`, but this can be changed if you want to more accurately calculate the LED's physical `{ x, y }` positions. Keyboard designers can implement `#define RGB_MATRIX_CENTER { 112, 32 }` in their config.h file with the new center point of the keyboard, or where they want it to be allowing more possibilities for the `{ x, y }` values. Do note that the maximum value for x or y is 255, and the recommended maximum is 224 as this gives animations runoff room before they reset. + +`// LED Index to Flag` is a bitmask, whether or not a certain LEDs is of a certain type. It is recommended that LEDs are set to only 1 type. + +## Flags {#flags} + +|Define |Value |Description | +|----------------------------|------|-------------------------------------------------| +|`HAS_FLAGS(bits, flags)` |*n/a* |Evaluates to `true` if `bits` has all `flags` set| +|`HAS_ANY_FLAGS(bits, flags)`|*n/a* |Evaluates to `true` if `bits` has any `flags` set| +|`LED_FLAG_NONE` |`0x00`|If this LED has no flags | +|`LED_FLAG_ALL` |`0xFF`|If this LED has all flags | +|`LED_FLAG_MODIFIER` |`0x01`|If the LED is on a modifier key | +|`LED_FLAG_UNDERGLOW` |`0x02`|If the LED is for underglow | +|`LED_FLAG_KEYLIGHT` |`0x04`|If the LED is for key backlight | +|`LED_FLAG_INDICATOR` |`0x08`|If the LED is for keyboard state indication | + +## Keycodes {#keycodes} + +All RGB keycodes are currently shared with the RGBLIGHT system: + +|Key |Aliases |Description | +|-------------------|----------|--------------------------------------------------------------------------------------| +|`RGB_TOG` | |Toggle RGB lighting on or off | +|`RGB_MODE_FORWARD` |`RGB_MOD` |Cycle through modes, reverse direction when Shift is held | +|`RGB_MODE_REVERSE` |`RGB_RMOD`|Cycle through modes in reverse, forward direction when Shift is held | +|`RGB_HUI` | |Increase hue, decrease hue when Shift is held | +|`RGB_HUD` | |Decrease hue, increase hue when Shift is held | +|`RGB_SAI` | |Increase saturation, decrease saturation when Shift is held | +|`RGB_SAD` | |Decrease saturation, increase saturation when Shift is held | +|`RGB_VAI` | |Increase value (brightness), decrease value when Shift is held | +|`RGB_VAD` | |Decrease value (brightness), increase value when Shift is held | +|`RGB_SPI` | |Increase effect speed (does not support eeprom yet), decrease speed when Shift is held| +|`RGB_SPD` | |Decrease effect speed (does not support eeprom yet), increase speed when Shift is held| +|`RGB_MODE_PLAIN` |`RGB_M_P` |Static (no animation) mode | +|`RGB_MODE_BREATHE` |`RGB_M_B` |Breathing animation mode | +|`RGB_MODE_RAINBOW` |`RGB_M_R` |Full gradient scrolling left to right (uses the `RGB_MATRIX_CYCLE_LEFT_RIGHT` mode) | +|`RGB_MODE_SWIRL` |`RGB_M_SW`|Full gradient spinning pinwheel around center of keyboard (uses `RGB_MATRIX_CYCLE_PINWHEEL` mode) | + +* `RGB_MODE_*` keycodes will generally work, but not all of the modes are currently mapped to the correct effects for the RGB Matrix system. + +`RGB_MODE_PLAIN`, `RGB_MODE_BREATHE`, `RGB_MODE_RAINBOW`, and `RGB_MODE_SWIRL` are the only ones that are mapped properly. The rest don't have a direct equivalent, and are not mapped. + +::: tip +`RGB_*` keycodes cannot be used with functions like `tap_code16(RGB_HUD)` as they're not USB HID keycodes. If you wish to replicate similar behaviour in custom code within your firmware (e.g. inside `encoder_update_user()` or `process_record_user()`), the equivalent [RGB functions](#functions) should be used instead. +::: + + +::: warning +By default, if you have both the [RGB Light](rgblight) and the RGB Matrix feature enabled, these keycodes will work for both features, at the same time. You can disable the keycode functionality by defining the `*_DISABLE_KEYCODES` option for the specific feature. +::: + +## RGB Matrix Effects {#rgb-matrix-effects} + +All effects have been configured to support current configuration values (Hue, Saturation, Value, & Speed) unless otherwise noted below. These are the effects that are currently available: + +```c +enum rgb_matrix_effects { + RGB_MATRIX_NONE = 0, + RGB_MATRIX_SOLID_COLOR = 1, // Static single hue, no speed support + RGB_MATRIX_ALPHAS_MODS, // Static dual hue, speed is hue for secondary hue + RGB_MATRIX_GRADIENT_UP_DOWN, // Static gradient top to bottom, speed controls how much gradient changes + RGB_MATRIX_GRADIENT_LEFT_RIGHT, // Static gradient left to right, speed controls how much gradient changes + RGB_MATRIX_BREATHING, // Single hue brightness cycling animation + RGB_MATRIX_BAND_SAT, // Single hue band fading saturation scrolling left to right + RGB_MATRIX_BAND_VAL, // Single hue band fading brightness scrolling left to right + RGB_MATRIX_BAND_PINWHEEL_SAT, // Single hue 3 blade spinning pinwheel fades saturation + RGB_MATRIX_BAND_PINWHEEL_VAL, // Single hue 3 blade spinning pinwheel fades brightness + RGB_MATRIX_BAND_SPIRAL_SAT, // Single hue spinning spiral fades saturation + RGB_MATRIX_BAND_SPIRAL_VAL, // Single hue spinning spiral fades brightness + RGB_MATRIX_CYCLE_ALL, // Full keyboard solid hue cycling through full gradient + RGB_MATRIX_CYCLE_LEFT_RIGHT, // Full gradient scrolling left to right + RGB_MATRIX_CYCLE_UP_DOWN, // Full gradient scrolling top to bottom + RGB_MATRIX_CYCLE_OUT_IN, // Full gradient scrolling out to in + RGB_MATRIX_CYCLE_OUT_IN_DUAL, // Full dual gradients scrolling out to in + RGB_MATRIX_RAINBOW_MOVING_CHEVRON, // Full gradient Chevron shapped scrolling left to right + RGB_MATRIX_CYCLE_PINWHEEL, // Full gradient spinning pinwheel around center of keyboard + RGB_MATRIX_CYCLE_SPIRAL, // Full gradient spinning spiral around center of keyboard + RGB_MATRIX_DUAL_BEACON, // Full gradient spinning around center of keyboard + RGB_MATRIX_RAINBOW_BEACON, // Full tighter gradient spinning around center of keyboard + RGB_MATRIX_RAINBOW_PINWHEELS, // Full dual gradients spinning two halfs of keyboard + RGB_MATRIX_FLOWER_BLOOMING, // Full tighter gradient of first half scrolling left to right and second half scrolling right to left + RGB_MATRIX_RAINDROPS, // Randomly changes a single key's hue + RGB_MATRIX_JELLYBEAN_RAINDROPS, // Randomly changes a single key's hue and saturation + RGB_MATRIX_HUE_BREATHING, // Hue shifts up a slight ammount at the same time, then shifts back + RGB_MATRIX_HUE_PENDULUM, // Hue shifts up a slight ammount in a wave to the right, then back to the left + RGB_MATRIX_HUE_WAVE, // Hue shifts up a slight ammount and then back down in a wave to the right + RGB_MATRIX_PIXEL_FRACTAL, // Single hue fractal filled keys pulsing horizontally out to edges + RGB_MATRIX_PIXEL_FLOW, // Pulsing RGB flow along LED wiring with random hues + RGB_MATRIX_PIXEL_RAIN, // Randomly light keys with random hues + RGB_MATRIX_TYPING_HEATMAP, // How hot is your WPM! + RGB_MATRIX_DIGITAL_RAIN, // That famous computer simulation + RGB_MATRIX_SOLID_REACTIVE_SIMPLE, // Pulses keys hit to hue & value then fades value out + RGB_MATRIX_SOLID_REACTIVE, // Static single hue, pulses keys hit to shifted hue then fades to current hue + RGB_MATRIX_SOLID_REACTIVE_WIDE, // Hue & value pulse near a single key hit then fades value out + RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE, // Hue & value pulse near multiple key hits then fades value out + RGB_MATRIX_SOLID_REACTIVE_CROSS, // Hue & value pulse the same column and row of a single key hit then fades value out + RGB_MATRIX_SOLID_REACTIVE_MULTICROSS, // Hue & value pulse the same column and row of multiple key hits then fades value out + RGB_MATRIX_SOLID_REACTIVE_NEXUS, // Hue & value pulse away on the same column and row of a single key hit then fades value out + RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS, // Hue & value pulse away on the same column and row of multiple key hits then fades value out + RGB_MATRIX_SPLASH, // Full gradient & value pulse away from a single key hit then fades value out + RGB_MATRIX_MULTISPLASH, // Full gradient & value pulse away from multiple key hits then fades value out + RGB_MATRIX_SOLID_SPLASH, // Hue & value pulse away from a single key hit then fades value out + RGB_MATRIX_SOLID_MULTISPLASH, // Hue & value pulse away from multiple key hits then fades value out + RGB_MATRIX_STARLIGHT, // LEDs turn on and off at random at varying brightness, maintaining user set color + RGB_MATRIX_STARLIGHT_DUAL_HUE, // LEDs turn on and off at random at varying brightness, modifies user set hue by +- 30 + RGB_MATRIX_STARLIGHT_DUAL_SAT, // LEDs turn on and off at random at varying brightness, modifies user set saturation by +- 30 + RGB_MATRIX_RIVERFLOW, // Modification to breathing animation, offset's animation depending on key location to simulate a river flowing + RGB_MATRIX_EFFECT_MAX +}; +``` + +You can enable a single effect by defining `ENABLE_[EFFECT_NAME]` in your `config.h`: + + +|Define |Description | +|------------------------------------------------------|----------------------------------------------| +|`#define ENABLE_RGB_MATRIX_ALPHAS_MODS` |Enables `RGB_MATRIX_ALPHAS_MODS` | +|`#define ENABLE_RGB_MATRIX_GRADIENT_UP_DOWN` |Enables `RGB_MATRIX_GRADIENT_UP_DOWN` | +|`#define ENABLE_RGB_MATRIX_GRADIENT_LEFT_RIGHT` |Enables `RGB_MATRIX_GRADIENT_LEFT_RIGHT` | +|`#define ENABLE_RGB_MATRIX_BREATHING` |Enables `RGB_MATRIX_BREATHING` | +|`#define ENABLE_RGB_MATRIX_BAND_SAT` |Enables `RGB_MATRIX_BAND_SAT` | +|`#define ENABLE_RGB_MATRIX_BAND_VAL` |Enables `RGB_MATRIX_BAND_VAL` | +|`#define ENABLE_RGB_MATRIX_BAND_PINWHEEL_SAT` |Enables `RGB_MATRIX_BAND_PINWHEEL_SAT` | +|`#define ENABLE_RGB_MATRIX_BAND_PINWHEEL_VAL` |Enables `RGB_MATRIX_BAND_PINWHEEL_VAL` | +|`#define ENABLE_RGB_MATRIX_BAND_SPIRAL_SAT` |Enables `RGB_MATRIX_BAND_SPIRAL_SAT` | +|`#define ENABLE_RGB_MATRIX_BAND_SPIRAL_VAL` |Enables `RGB_MATRIX_BAND_SPIRAL_VAL` | +|`#define ENABLE_RGB_MATRIX_CYCLE_ALL` |Enables `RGB_MATRIX_CYCLE_ALL` | +|`#define ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT` |Enables `RGB_MATRIX_CYCLE_LEFT_RIGHT` | +|`#define ENABLE_RGB_MATRIX_CYCLE_UP_DOWN` |Enables `RGB_MATRIX_CYCLE_UP_DOWN` | +|`#define ENABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON` |Enables `RGB_MATRIX_RAINBOW_MOVING_CHEVRON` | +|`#define ENABLE_RGB_MATRIX_CYCLE_OUT_IN` |Enables `RGB_MATRIX_CYCLE_OUT_IN` | +|`#define ENABLE_RGB_MATRIX_CYCLE_OUT_IN_DUAL` |Enables `RGB_MATRIX_CYCLE_OUT_IN_DUAL` | +|`#define ENABLE_RGB_MATRIX_CYCLE_PINWHEEL` |Enables `RGB_MATRIX_CYCLE_PINWHEEL` | +|`#define ENABLE_RGB_MATRIX_CYCLE_SPIRAL` |Enables `RGB_MATRIX_CYCLE_SPIRAL` | +|`#define ENABLE_RGB_MATRIX_DUAL_BEACON` |Enables `RGB_MATRIX_DUAL_BEACON` | +|`#define ENABLE_RGB_MATRIX_RAINBOW_BEACON` |Enables `RGB_MATRIX_RAINBOW_BEACON` | +|`#define ENABLE_RGB_MATRIX_RAINBOW_PINWHEELS` |Enables `RGB_MATRIX_RAINBOW_PINWHEELS` | +|`#define ENABLE_RGB_MATRIX_FLOWER_BLOOMING` |Enables `RGB_MATRIX_FLOWER_BLOOMING` | +|`#define ENABLE_RGB_MATRIX_RAINDROPS` |Enables `RGB_MATRIX_RAINDROPS` | +|`#define ENABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS` |Enables `RGB_MATRIX_JELLYBEAN_RAINDROPS` | +|`#define ENABLE_RGB_MATRIX_HUE_BREATHING` |Enables `RGB_MATRIX_HUE_BREATHING` | +|`#define ENABLE_RGB_MATRIX_HUE_PENDULUM` |Enables `RGB_MATRIX_HUE_PENDULUM` | +|`#define ENABLE_RGB_MATRIX_HUE_WAVE` |Enables `RGB_MATRIX_HUE_WAVE ` | +|`#define ENABLE_RGB_MATRIX_PIXEL_FRACTAL` |Enables `RGB_MATRIX_PIXEL_FRACTAL` | +|`#define ENABLE_RGB_MATRIX_PIXEL_FLOW` |Enables `RGB_MATRIX_PIXEL_FLOW` | +|`#define ENABLE_RGB_MATRIX_PIXEL_RAIN` |Enables `RGB_MATRIX_PIXEL_RAIN` | +|`#define ENABLE_RGB_MATRIX_STARLIGHT` |Enables `RGB_MATRIX_STARLIGHT` | +|`#define ENABLE_RGB_MATRIX_STARLIGHT_DUAL_HUE` |Enables `RGB_MATRIX_STARLIGHT_DUAL_HUE` | +|`#define ENABLE_RGB_MATRIX_STARLIGHT_DUAL_SAT` |Enables `RGB_MATRIX_STARLIGHT_DUAL_SAT` | +|`#define ENABLE_RGB_MATRIX_RIVERFLOW` |Enables `RGB_MATRIX_RIVERFLOW` | + +|Framebuffer Defines |Description | +|------------------------------------------------------|----------------------------------------------| +|`#define ENABLE_RGB_MATRIX_TYPING_HEATMAP` |Enables `RGB_MATRIX_TYPING_HEATMAP` | +|`#define ENABLE_RGB_MATRIX_DIGITAL_RAIN` |Enables `RGB_MATRIX_DIGITAL_RAIN` | + +::: tip +These modes introduce additional logic that can increase firmware size. +::: + +|Reactive Defines |Description | +|------------------------------------------------------|----------------------------------------------| +|`#define ENABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE` |Enables `RGB_MATRIX_SOLID_REACTIVE_SIMPLE` | +|`#define ENABLE_RGB_MATRIX_SOLID_REACTIVE` |Enables `RGB_MATRIX_SOLID_REACTIVE` | +|`#define ENABLE_RGB_MATRIX_SOLID_REACTIVE_WIDE` |Enables `RGB_MATRIX_SOLID_REACTIVE_WIDE` | +|`#define ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE` |Enables `RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE` | +|`#define ENABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS` |Enables `RGB_MATRIX_SOLID_REACTIVE_CROSS` | +|`#define ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTICROSS` |Enables `RGB_MATRIX_SOLID_REACTIVE_MULTICROSS`| +|`#define ENABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS` |Enables `RGB_MATRIX_SOLID_REACTIVE_NEXUS` | +|`#define ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS` |Enables `RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS`| +|`#define ENABLE_RGB_MATRIX_SPLASH` |Enables `RGB_MATRIX_SPLASH` | +|`#define ENABLE_RGB_MATRIX_MULTISPLASH` |Enables `RGB_MATRIX_MULTISPLASH` | +|`#define ENABLE_RGB_MATRIX_SOLID_SPLASH` |Enables `RGB_MATRIX_SOLID_SPLASH` | +|`#define ENABLE_RGB_MATRIX_SOLID_MULTISPLASH` |Enables `RGB_MATRIX_SOLID_MULTISPLASH` | + +::: tip +These modes introduce additional logic that can increase firmware size. +::: + + +### RGB Matrix Effect Typing Heatmap {#rgb-matrix-effect-typing-heatmap} + +This effect will color the RGB matrix according to a heatmap of recently pressed keys. Whenever a key is pressed its "temperature" increases as well as that of its neighboring keys. The temperature of each key is then decreased automatically every 25 milliseconds by default. + +In order to change the delay of temperature decrease define `RGB_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS`: + +```c +#define RGB_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS 50 +``` + +As heatmap uses the physical position of the leds set in the g_led_config, you may need to tweak the following options to get the best effect for your keyboard. Note the size of this grid is `224x64`. + +Limit the distance the effect spreads to surrounding keys. + +```c +#define RGB_MATRIX_TYPING_HEATMAP_SPREAD 40 +``` + +Limit how hot surrounding keys get from each press. + +```c +#define RGB_MATRIX_TYPING_HEATMAP_AREA_LIMIT 16 +``` + +Remove the spread effect entirely. + +```c +#define RGB_MATRIX_TYPING_HEATMAP_SLIM +``` + +It's also possible to adjust the tempo of *heating up*. It's defined as the number of shades that are +increased on the [HSV scale](https://en.wikipedia.org/wiki/HSL_and_HSV). Decreasing this value increases +the number of keystrokes needed to fully heat up the key. + +```c +#define RGB_MATRIX_TYPING_HEATMAP_INCREASE_STEP 32 +``` + +### RGB Matrix Effect Solid Reactive {#rgb-matrix-effect-solid-reactive} + +Solid reactive effects will pulse RGB light on key presses with user configurable hues. To enable gradient mode that will automatically change reactive color, add the following define: + +```c +#define RGB_MATRIX_SOLID_REACTIVE_GRADIENT_MODE +``` + +Gradient mode will loop through the color wheel hues over time and its duration can be controlled with the effect speed keycodes (`RGB_SPI`/`RGB_SPD`). + +## Custom RGB Matrix Effects {#custom-rgb-matrix-effects} + +By setting `RGB_MATRIX_CUSTOM_USER = yes` in `rules.mk`, new effects can be defined directly from your keymap or userspace, without having to edit any QMK core files. To declare new effects, create a `rgb_matrix_user.inc` file in the user keymap directory or userspace folder. + +::: tip +Hardware maintainers who want to limit custom effects to a specific keyboard can create a `rgb_matrix_kb.inc` file in the root of the keyboard directory, and add `RGB_MATRIX_CUSTOM_KB = yes` to the keyboard level `rules.mk`. +::: + +To use custom effects in your code, simply prepend `RGB_MATRIX_CUSTOM_` to the effect name specified in `RGB_MATRIX_EFFECT()`. For example, an effect declared as `RGB_MATRIX_EFFECT(my_cool_effect)` would be referenced with: + +```c +rgb_matrix_mode(RGB_MATRIX_CUSTOM_my_cool_effect); +``` + +```c +// !!! DO NOT ADD #pragma once !!! // + +// Step 1. +// Declare custom effects using the RGB_MATRIX_EFFECT macro +// (note the lack of semicolon after the macro!) +RGB_MATRIX_EFFECT(my_cool_effect) +RGB_MATRIX_EFFECT(my_cool_effect2) + +// Step 2. +// Define effects inside the `RGB_MATRIX_CUSTOM_EFFECT_IMPLS` ifdef block +#ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS + +// e.g: A simple effect, self-contained within a single method +static bool my_cool_effect(effect_params_t* params) { + RGB_MATRIX_USE_LIMITS(led_min, led_max); + for (uint8_t i = led_min; i < led_max; i++) { + rgb_matrix_set_color(i, 0xff, 0xff, 0x00); + } + return rgb_matrix_check_finished_leds(led_max); +} + +// e.g: A more complex effect, relying on external methods and state, with +// dedicated init and run methods +static uint8_t some_global_state; +static void my_cool_effect2_complex_init(effect_params_t* params) { + some_global_state = 1; +} +static bool my_cool_effect2_complex_run(effect_params_t* params) { + RGB_MATRIX_USE_LIMITS(led_min, led_max); + for (uint8_t i = led_min; i < led_max; i++) { + rgb_matrix_set_color(i, 0xff, some_global_state++, 0xff); + } + return rgb_matrix_check_finished_leds(led_max); +} +static bool my_cool_effect2(effect_params_t* params) { + if (params->init) my_cool_effect2_complex_init(params); + return my_cool_effect2_complex_run(params); +} + +#endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS +``` + +For inspiration and examples, check out the built-in effects under `quantum/rgb_matrix/animations/`. + + +## Colors {#colors} + +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_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 {#additional-configh-options} + +```c +#define RGB_MATRIX_KEYRELEASES // reactive effects respond to keyreleases (instead of keypresses) +#define RGB_MATRIX_TIMEOUT 0 // number of milliseconds to wait until rgb automatically turns off +#define RGB_MATRIX_SLEEP // turn off effects when suspended +#define RGB_MATRIX_LED_PROCESS_LIMIT (RGB_MATRIX_LED_COUNT + 4) / 5 // limits the number of LEDs to process in an animation per task run (increases keyboard responsiveness) +#define RGB_MATRIX_LED_FLUSH_LIMIT 16 // limits in milliseconds how frequently an animation will update the LEDs. 16 (16ms) is equivalent to limiting to 60fps (increases keyboard responsiveness) +#define RGB_MATRIX_MAXIMUM_BRIGHTNESS 200 // limits maximum brightness of LEDs to 200 out of 255. If not defined maximum brightness is set to 255 +#define RGB_MATRIX_DEFAULT_ON true // Sets the default enabled state, if none has been set +#define RGB_MATRIX_DEFAULT_MODE RGB_MATRIX_CYCLE_LEFT_RIGHT // Sets the default mode, if none has been set +#define RGB_MATRIX_DEFAULT_HUE 0 // Sets the default hue value, if none has been set +#define RGB_MATRIX_DEFAULT_SAT 255 // Sets the default saturation value, if none has been set +#define RGB_MATRIX_DEFAULT_VAL RGB_MATRIX_MAXIMUM_BRIGHTNESS // Sets the default brightness value, if none has been set +#define RGB_MATRIX_DEFAULT_SPD 127 // Sets the default animation speed, if none has been set +#define RGB_MATRIX_DEFAULT_FLAGS LED_FLAG_ALL // Sets the default LED flags, if none has been set +#define RGB_MATRIX_DISABLE_KEYCODES // disables control of rgb matrix by keycodes (must use code functions to control the feature) +#define RGB_MATRIX_SPLIT { X, Y } // (Optional) For split keyboards, the number of LEDs connected on each half. X = left, Y = Right. + // If reactive effects are enabled, you also will want to enable SPLIT_TRANSPORT_MIRROR +#define RGB_TRIGGER_ON_KEYDOWN // Triggers RGB keypress events on key down. This makes RGB control feel more responsive. This may cause RGB to not function properly on some boards +``` + +## EEPROM storage {#eeprom-storage} + +The EEPROM for it is currently shared with the LED Matrix system (it's generally assumed only one feature would be used at a time). + +## Functions {#functions} + +### Direct Operation {#direct-operation} +|Function |Description | +|--------------------------------------------|-------------| +|`rgb_matrix_set_color_all(r, g, b)` |Set all of the LEDs to the given RGB value, where `r`/`g`/`b` are between 0 and 255 (not written to EEPROM) | +|`rgb_matrix_set_color(index, r, g, b)` |Set a single LED to the given RGB value, where `r`/`g`/`b` are between 0 and 255, and `index` is between 0 and `RGB_MATRIX_LED_COUNT` (not written to EEPROM) | + +### Disable/Enable Effects {#disable-enable-effects} +|Function |Description | +|--------------------------------------------|-------------| +|`rgb_matrix_toggle()` |Toggle effect range LEDs between on and off | +|`rgb_matrix_toggle_noeeprom()` |Toggle effect range LEDs between on and off (not written to EEPROM) | +|`rgb_matrix_enable()` |Turn effect range LEDs on, based on their previous state | +|`rgb_matrix_enable_noeeprom()` |Turn effect range LEDs on, based on their previous state (not written to EEPROM) | +|`rgb_matrix_disable()` |Turn effect range LEDs off, based on their previous state | +|`rgb_matrix_disable_noeeprom()` |Turn effect range LEDs off, based on their previous state (not written to EEPROM) | + +### Change Effect Mode {#change-effect-mode} +|Function |Description | +|--------------------------------------------|-------------| +|`rgb_matrix_mode(mode)` |Set the mode, if RGB animations are enabled | +|`rgb_matrix_mode_noeeprom(mode)` |Set the mode, if RGB animations are enabled (not written to EEPROM) | +|`rgb_matrix_step()` |Change the mode to the next RGB animation in the list of enabled RGB animations | +|`rgb_matrix_step_noeeprom()` |Change the mode to the next RGB animation in the list of enabled RGB animations (not written to EEPROM) | +|`rgb_matrix_step_reverse()` |Change the mode to the previous RGB animation in the list of enabled RGB animations | +|`rgb_matrix_step_reverse_noeeprom()` |Change the mode to the previous RGB animation in the list of enabled RGB animations (not written to EEPROM) | +|`rgb_matrix_increase_speed()` |Increase the speed of the animations | +|`rgb_matrix_increase_speed_noeeprom()` |Increase the speed of the animations (not written to EEPROM) | +|`rgb_matrix_decrease_speed()` |Decrease the speed of the animations | +|`rgb_matrix_decrease_speed_noeeprom()` |Decrease the speed of the animations (not written to EEPROM) | +|`rgb_matrix_set_speed(speed)` |Set the speed of the animations to the given value where `speed` is between 0 and 255 | +|`rgb_matrix_set_speed_noeeprom(speed)` |Set the speed of the animations to the given value where `speed` is between 0 and 255 (not written to EEPROM) | +|`rgb_matrix_reload_from_eeprom()` |Reload the effect configuration (enabled, mode and color) from EEPROM | + +### Change Color {#change-color} +|Function |Description | +|--------------------------------------------|-------------| +|`rgb_matrix_increase_hue()` |Increase the hue for effect range LEDs. This wraps around at maximum hue | +|`rgb_matrix_increase_hue_noeeprom()` |Increase the hue for effect range LEDs. This wraps around at maximum hue (not written to EEPROM) | +|`rgb_matrix_decrease_hue()` |Decrease the hue for effect range LEDs. This wraps around at minimum hue | +|`rgb_matrix_decrease_hue_noeeprom()` |Decrease the hue for effect range LEDs. This wraps around at minimum hue (not written to EEPROM) | +|`rgb_matrix_increase_sat()` |Increase the saturation for effect range LEDs. This wraps around at maximum saturation | +|`rgb_matrix_increase_sat_noeeprom()` |Increase the saturation for effect range LEDs. This wraps around at maximum saturation (not written to EEPROM) | +|`rgb_matrix_decrease_sat()` |Decrease the saturation for effect range LEDs. This wraps around at minimum saturation | +|`rgb_matrix_decrease_sat_noeeprom()` |Decrease the saturation for effect range LEDs. This wraps around at minimum saturation (not written to EEPROM) | +|`rgb_matrix_increase_val()` |Increase the value for effect range LEDs. This wraps around at maximum value | +|`rgb_matrix_increase_val_noeeprom()` |Increase the value for effect range LEDs. This wraps around at maximum value (not written to EEPROM) | +|`rgb_matrix_decrease_val()` |Decrease the value for effect range LEDs. This wraps around at minimum value | +|`rgb_matrix_decrease_val_noeeprom()` |Decrease the value for effect range LEDs. This wraps around at minimum value (not written to EEPROM) | +|`rgb_matrix_sethsv(h, s, v)` |Set LEDs to the given HSV value where `h`/`s`/`v` are between 0 and 255 | +|`rgb_matrix_sethsv_noeeprom(h, s, v)` |Set LEDs to the given HSV value where `h`/`s`/`v` are between 0 and 255 (not written to EEPROM) | + +### Query Current Status {#query-current-status} +|Function |Description | +|---------------------------------|---------------------------| +|`rgb_matrix_is_enabled()` |Gets current on/off status | +|`rgb_matrix_get_mode()` |Gets current mode | +|`rgb_matrix_get_hue()` |Gets current hue | +|`rgb_matrix_get_sat()` |Gets current sat | +|`rgb_matrix_get_val()` |Gets current val | +|`rgb_matrix_get_hsv()` |Gets hue, sat, and val and returns a [`HSV` structure](https://github.com/qmk/qmk_firmware/blob/7ba6456c0b2e041bb9f97dbed265c5b8b4b12192/quantum/color.h#L56-L61)| +|`rgb_matrix_get_speed()` |Gets current speed | +|`rgb_matrix_get_suspend_state()` |Gets current suspend state | + +## Callbacks {#callbacks} + +### Indicators {#indicators} + +If you want to set custom indicators, such as an LED for Caps Lock, or layer indication, then you can use the `rgb_matrix_indicators_kb` function on the keyboard level source file, or `rgb_matrix_indicators_user` function in the user `keymap.c`. +```c +bool rgb_matrix_indicators_kb(void) { + if (!rgb_matrix_indicators_user()) { + return false; + } + rgb_matrix_set_color(index, red, green, blue); + return true; +} +``` + +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 +bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) { + RGB_MATRIX_INDICATOR_SET_COLOR(index, red, green, blue); + return false; +} +``` + +### Indicator Examples {#indicator-examples} + +Caps Lock indicator on alphanumeric flagged keys: +```c +bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) { + if (host_keyboard_led_state().caps_lock) { + for (uint8_t i = led_min; i < led_max; i++) { + if (g_led_config.flags[i] & LED_FLAG_KEYLIGHT) { + rgb_matrix_set_color(i, RGB_RED); + } + } + } + return false; +} +``` + +Layer indicator on all keys: +```c +bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) { + for (uint8_t i = led_min; i < led_max; i++) { + switch(get_highest_layer(layer_state|default_layer_state)) { + case 2: + rgb_matrix_set_color(i, RGB_BLUE); + break; + case 1: + rgb_matrix_set_color(i, RGB_YELLOW); + break; + default: + break; + } + } + return false; +} +``` + +Layer indicator only on keys with configured keycodes: +```c +bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) { + if (get_highest_layer(layer_state) > 0) { + uint8_t layer = get_highest_layer(layer_state); + + for (uint8_t row = 0; row < MATRIX_ROWS; ++row) { + for (uint8_t col = 0; col < MATRIX_COLS; ++col) { + uint8_t index = g_led_config.matrix_co[row][col]; + + if (index >= led_min && index < led_max && index != NO_LED && + keymap_key_to_keycode(layer, (keypos_t){col,row}) > KC_TRNS) { + rgb_matrix_set_color(index, RGB_GREEN); + } + } + } + } + return false; +} +``` + +::: tip +Split keyboards will require layer state data syncing with `#define SPLIT_LAYER_STATE_ENABLE`. See [Data Sync Options](split_keyboard#data-sync-options) for more details. +::: + +#### Examples {#indicator-examples-2} + +This example sets the modifiers to be a specific color based on the layer state. You can use a switch case here, instead, if you would like. This uses HSV and then converts to RGB, because this allows the brightness to be limited (important when using the WS2812 driver). + +```c +bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) { + HSV hsv = {0, 255, 255}; + + if (layer_state_is(layer_state, 2)) { + hsv = {130, 255, 255}; + } else { + hsv = {30, 255, 255}; + } + + if (hsv.v > rgb_matrix_get_val()) { + hsv.v = rgb_matrix_get_val(); + } + RGB rgb = hsv_to_rgb(hsv); + + for (uint8_t i = led_min; i < led_max; i++) { + if (HAS_FLAGS(g_led_config.flags[i], 0x01)) { // 0x01 == LED_FLAG_MODIFIER + rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); + } + } + return false; +} +``` + +If you want to indicate a Host LED status (caps lock, num lock, etc), you can use something like this to light up the caps lock key: + +```c +bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) { + if (host_keyboard_led_state().caps_lock) { + RGB_MATRIX_INDICATOR_SET_COLOR(5, 255, 255, 255); // assuming caps lock is at led #5 + } else { + RGB_MATRIX_INDICATOR_SET_COLOR(5, 0, 0, 0); + } + return false; +} +``` + +::: tip +RGB indicators on split keyboards will require state information synced to the slave half (e.g. `#define SPLIT_LAYER_STATE_ENABLE`). See [data sync options](split_keyboard#data-sync-options) for more details. +::: + +#### Indicators without RGB Matrix Effect + +If you want to just use RGB indicators without RGB matrix effect, it is not possible to disable the latter because toggling RGB off will disable everything. You can workaround it with solid effect and colors off using this init function: +```c +void keyboard_post_init_user(void) { + rgb_matrix_mode_noeeprom(RGB_MATRIX_SOLID_COLOR); + rgb_matrix_sethsv_noeeprom(HSV_OFF); +} +``` diff --git a/docs/features/rgblight.md b/docs/features/rgblight.md new file mode 100644 index 0000000000..ece1c10467 --- /dev/null +++ b/docs/features/rgblight.md @@ -0,0 +1,581 @@ +# RGB Lighting + +QMK has the ability to control RGB LEDs attached to your keyboard. This is commonly called *underglow*, due to the LEDs often being mounted on the bottom of the keyboard, producing a nice diffused effect when combined with a translucent case. + +![Planck with RGB Underglow](https://raw.githubusercontent.com/qmk/qmk_firmware/3774a7fcdab5544fc787f4c200be05fcd417e31f/keyboards/planck/keymaps/yang/planck-with-rgb-underglow.jpg) + +Some keyboards come with RGB LEDs preinstalled. Others must have them installed after the fact. See the [Hardware Modification](#hardware-modification) section for information on adding RGB lighting to your keyboard. + +Currently QMK supports the following addressable LEDs: + + * WS2811, WS2812, WS2812B, WS2812C, etc. + * SK6812, SK6812MINI, SK6805 + * APA102 + +These LEDs are called "addressable" because instead of using a wire per color, each LED contains a small microchip that understands a special protocol sent over a single wire. The chip passes on the remaining data to the next LED, allowing them to be chained together. In this way, you can easily control the color of the individual LEDs. + +## Usage + +On keyboards with onboard RGB LEDs, it is usually enabled by default. If it is not working for you, check that your `rules.mk` includes the following: + +```make +RGBLIGHT_ENABLE = yes +``` + +::: tip +There are additional configuration options for ARM controllers that offer increased performance over the default WS2812 bitbang driver. Please see [WS2812 Driver](../drivers/ws2812) for more information. +::: + +For APA102 LEDs, add the following to your `rules.mk`: + +```make +RGBLIGHT_ENABLE = yes +RGBLIGHT_DRIVER = apa102 +``` + +At minimum you must define the data pin your LED strip is connected to, and the number of LEDs in the strip, in your `config.h`. For APA102 LEDs, you must also define the clock pin. If your keyboard has onboard RGB LEDs, and you are simply creating a keymap, you usually won't need to modify these. + +|Define |Description | +|--------------------|-------------------------------------------------------------------------| +|`WS2812_DI_PIN` |The pin connected to the data pin of the LEDs (WS2812) | +|`APA102_DI_PIN` |The pin connected to the data pin of the LEDs (APA102) | +|`APA102_CI_PIN` |The pin connected to the clock pin of the LEDs (APA102) | +|`RGBLIGHT_LED_COUNT`|The number of LEDs connected | +|`RGBLED_SPLIT` |(Optional) For split keyboards, the number of LEDs connected on each half| + +Then you should be able to use the keycodes below to change the RGB lighting to your liking. + +### Color Selection + +QMK uses [Hue, Saturation, and Value](https://en.wikipedia.org/wiki/HSL_and_HSV) to select colors rather than RGB. The color wheel below demonstrates how this works. + +HSV Color Wheel + +Changing the **Hue** cycles around the circle.
+Changing the **Saturation** moves between the inner and outer sections of the wheel, affecting the intensity of the color.
+Changing the **Value** sets the overall brightness.
+ +![QMK Color Wheel with HSV Values](https://i.imgur.com/vkYVo66.jpg) + +## Keycodes + +|Key |Aliases |Description | +|-------------------|----------|--------------------------------------------------------------------| +|`RGB_TOG` | |Toggle RGB lighting on or off | +|`RGB_MODE_FORWARD` |`RGB_MOD` |Cycle through modes, reverse direction when Shift is held | +|`RGB_MODE_REVERSE` |`RGB_RMOD`|Cycle through modes in reverse, forward direction when Shift is held| +|`RGB_HUI` | |Increase hue, decrease hue when Shift is held | +|`RGB_HUD` | |Decrease hue, increase hue when Shift is held | +|`RGB_SAI` | |Increase saturation, decrease saturation when Shift is held | +|`RGB_SAD` | |Decrease saturation, increase saturation when Shift is held | +|`RGB_VAI` | |Increase value (brightness), decrease value when Shift is held | +|`RGB_VAD` | |Decrease value (brightness), increase value when Shift is held | +|`RGB_MODE_PLAIN` |`RGB_M_P `|Static (no animation) mode | +|`RGB_MODE_BREATHE` |`RGB_M_B` |Breathing animation mode | +|`RGB_MODE_RAINBOW` |`RGB_M_R` |Rainbow animation mode | +|`RGB_MODE_SWIRL` |`RGB_M_SW`|Swirl animation mode | +|`RGB_MODE_SNAKE` |`RGB_M_SN`|Snake animation mode | +|`RGB_MODE_KNIGHT` |`RGB_M_K` |"Knight Rider" animation mode | +|`RGB_MODE_XMAS` |`RGB_M_X` |Christmas animation mode | +|`RGB_MODE_GRADIENT`|`RGB_M_G` |Static gradient animation mode | +|`RGB_MODE_RGBTEST` |`RGB_M_T` |Red, Green, Blue test animation mode | +|`RGB_MODE_TWINKLE` |`RGB_M_TW`|Twinkle animation mode | + +::: tip +`RGB_*` keycodes cannot be used with functions like `tap_code16(RGB_HUI)` as they're not USB HID keycodes. If you wish to replicate similar behaviour in custom code within your firmware (e.g. inside `encoder_update_user()` or `process_record_user()`), the equivalent [RGB functions](#functions) should be used instead. +::: + + +::: warning +By default, if you have both the RGB Light and the [RGB Matrix](rgb_matrix) feature enabled, these keycodes will work for both features, at the same time. You can disable the keycode functionality by defining the `*_DISABLE_KEYCODES` option for the specific feature. +::: + +## Configuration + +Your RGB lighting can be configured by placing these `#define`s in your `config.h`: + +|Define |Default |Description | +|---------------------------|----------------------------|---------------------------------------------------------------------------------------------------------------------------| +|`RGBLIGHT_HUE_STEP` |`8` |The number of steps to cycle through the hue by | +|`RGBLIGHT_SAT_STEP` |`17` |The number of steps to increment the saturation by | +|`RGBLIGHT_VAL_STEP` |`17` |The number of steps to increment the brightness by | +|`RGBLIGHT_LIMIT_VAL` |`255` |The maximum brightness level | +|`RGBLIGHT_SLEEP` |*Not defined* |If defined, the RGB lighting will be switched off when the host goes to sleep | +|`RGBLIGHT_SPLIT` |*Not defined* |If defined, synchronization functionality for split keyboards is added | +|`RGBLIGHT_DISABLE_KEYCODES`|*Not defined* |If defined, disables the ability to control RGB Light from the keycodes. You must use code functions to control the feature| +|`RGBLIGHT_DEFAULT_MODE` |`RGBLIGHT_MODE_STATIC_LIGHT`|The default mode to use upon clearing the EEPROM | +|`RGBLIGHT_DEFAULT_HUE` |`0` (red) |The default hue to use upon clearing the EEPROM | +|`RGBLIGHT_DEFAULT_SAT` |`UINT8_MAX` (255) |The default saturation to use upon clearing the EEPROM | +|`RGBLIGHT_DEFAULT_VAL` |`RGBLIGHT_LIMIT_VAL` |The default value (brightness) to use upon clearing the EEPROM | +|`RGBLIGHT_DEFAULT_SPD` |`0` |The default speed to use upon clearing the EEPROM | +|`RGBLIGHT_DEFAULT_ON` |`true` |Enable RGB lighting upon clearing the EEPROM | + +## Effects and Animations + +Not only can this lighting be whatever color you want, +if `RGBLIGHT_EFFECT_xxxx` is defined, you also have a number of animation modes at your disposal: + +|Mode number symbol |Additional number |Description | +|-----------------------------|-------------------|---------------------------------------| +|`RGBLIGHT_MODE_STATIC_LIGHT` | *None* |Solid color (this mode is always enabled) | +|`RGBLIGHT_MODE_BREATHING` | 0,1,2,3 |Solid color breathing | +|`RGBLIGHT_MODE_RAINBOW_MOOD` | 0,1,2 |Cycling rainbow | +|`RGBLIGHT_MODE_RAINBOW_SWIRL`| 0,1,2,3,4,5 |Swirling rainbow | +|`RGBLIGHT_MODE_SNAKE` | 0,1,2,3,4,5 |Snake | +|`RGBLIGHT_MODE_KNIGHT` | 0,1,2 |Knight | +|`RGBLIGHT_MODE_CHRISTMAS` | *None* |Christmas | +|`RGBLIGHT_MODE_STATIC_GRADIENT`| 0,1,..,9 |Static gradient | +|`RGBLIGHT_MODE_RGB_TEST` | *None* |RGB Test | +|`RGBLIGHT_MODE_ALTERNATING` | *None* |Alternating | +|`RGBLIGHT_MODE_TWINKLE` | 0,1,2,3,4,5 |Twinkle | + +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/rgblight.h` there is a contrast table between the old mode number and the current symbol. + + +### Effect and Animation Toggles + +Use these defines to add or remove animations from the firmware. When you are running low on flash space, it can be helpful to disable animations you are not using. + +|Define |Default |Description | +|------------------------------------|-------------|-------------------------------------------------------------------------| +|`RGBLIGHT_ANIMATIONS` |*Not defined*|Enable all additional animation modes. (deprecated) | +|`RGBLIGHT_EFFECT_ALTERNATING` |*Not defined*|Enable alternating animation mode. | +|`RGBLIGHT_EFFECT_BREATHING` |*Not defined*|Enable breathing animation mode. | +|`RGBLIGHT_EFFECT_CHRISTMAS` |*Not defined*|Enable christmas animation mode. | +|`RGBLIGHT_EFFECT_KNIGHT` |*Not defined*|Enable knight animation mode. | +|`RGBLIGHT_EFFECT_RAINBOW_MOOD` |*Not defined*|Enable rainbow mood animation mode. | +|`RGBLIGHT_EFFECT_RAINBOW_SWIRL` |*Not defined*|Enable rainbow swirl animation mode. | +|`RGBLIGHT_EFFECT_RGB_TEST` |*Not defined*|Enable RGB test animation mode. | +|`RGBLIGHT_EFFECT_SNAKE` |*Not defined*|Enable snake animation mode. | +|`RGBLIGHT_EFFECT_STATIC_GRADIENT` |*Not defined*|Enable static gradient mode. | +|`RGBLIGHT_EFFECT_TWINKLE` |*Not defined*|Enable twinkle animation mode. | + +::: warning +`RGBLIGHT_ANIMATIONS` is being deprecated and animation modes should be explicitly defined. +::: + +### Effect and Animation Settings + +The following options are used to tweak the various animations: + +|Define |Default |Description | +|------------------------------------|--------------------|-----------------------------------------------------------------------------------------------| +|`RGBLIGHT_EFFECT_BREATHE_CENTER` |*Not defined* |If defined, used to calculate the curve for the breathing animation. Valid values are 1.0 to 2.7 | +|`RGBLIGHT_EFFECT_BREATHE_MAX` |`255` |The maximum brightness for the breathing mode. Valid values are 1 to 255 | +|`RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL`|`40` |How long (in milliseconds) to wait between animation steps for the "Christmas" animation | +|`RGBLIGHT_EFFECT_CHRISTMAS_STEP` |`2` |The number of LEDs to group the red/green colors by for the "Christmas" animation | +|`RGBLIGHT_EFFECT_KNIGHT_LED_NUM` |`RGBLIGHT_LED_COUNT`|The number of LEDs to have the "Knight" animation travel | +|`RGBLIGHT_EFFECT_KNIGHT_LENGTH` |`3` |The number of LEDs to light up for the "Knight" animation | +|`RGBLIGHT_EFFECT_KNIGHT_OFFSET` |`0` |The number of LEDs to start the "Knight" animation from the start of the strip by | +|`RGBLIGHT_RAINBOW_SWIRL_RANGE` |`255` |Range adjustment for the rainbow swirl effect to get different swirls | +|`RGBLIGHT_EFFECT_SNAKE_LENGTH` |`4` |The number of LEDs to light up for the "Snake" animation | +|`RGBLIGHT_EFFECT_TWINKLE_LIFE` |`200` |Adjusts how quickly each LED brightens and dims when twinkling (in animation steps) | +|`RGBLIGHT_EFFECT_TWINKLE_PROBABILITY`|`1/127` |Adjusts how likely each LED is to twinkle (on each animation step) | + +### Example Usage to Reduce Memory Footprint + 1. Use `#undef` to selectively disable animations. The following would disable two animations and save about 4KiB: + +```diff + #undef RGBLIGHT_LED_COUNT ++#undef RGBLIGHT_EFFECT_STATIC_GRADIENT ++#undef RGBLIGHT_EFFECT_RAINBOW_SWIRL + #define RGBLIGHT_LED_COUNT 12 + #define RGBLIGHT_HUE_STEP 8 + #define RGBLIGHT_SAT_STEP 8 +``` + +### Animation Speed + +You can also modify the speeds that the different modes animate at: + +Here is a quick demo on Youtube (with NPKC KC60) (https://www.youtube.com/watch?v=VKrpPAHlisY). + +```c +// How long (in milliseconds) to wait between animation steps for each of the "Solid color breathing" animations +const uint8_t RGBLED_BREATHING_INTERVALS[] PROGMEM = {30, 20, 10, 5}; + +// How long (in milliseconds) to wait between animation steps for each of the "Cycling rainbow" animations +const uint8_t RGBLED_RAINBOW_MOOD_INTERVALS[] PROGMEM = {120, 60, 30}; + +// How long (in milliseconds) to wait between animation steps for each of the "Swirling rainbow" animations +const uint8_t RGBLED_RAINBOW_SWIRL_INTERVALS[] PROGMEM = {100, 50, 20}; + +// How long (in milliseconds) to wait between animation steps for each of the "Snake" animations +const uint8_t RGBLED_SNAKE_INTERVALS[] PROGMEM = {100, 50, 20}; + +// How long (in milliseconds) to wait between animation steps for each of the "Knight" animations +const uint8_t RGBLED_KNIGHT_INTERVALS[] PROGMEM = {127, 63, 31}; + +// How long (in milliseconds) to wait between animation steps for each of the "Twinkle" animations +const uint8_t RGBLED_TWINKLE_INTERVALS[] PROGMEM = {50, 25, 10}; + +// These control which hues are selected for each of the "Static gradient" modes +const uint8_t RGBLED_GRADIENT_RANGES[] PROGMEM = {255, 170, 127, 85, 64}; +``` + +## Lighting Layers + +::: tip +**Note:** Lighting Layers is an RGB Light feature, it will not work for RGB Matrix. See [RGB Matrix Indicators](rgb_matrix#indicators) for details on how to do so. +::: + +By including `#define RGBLIGHT_LAYERS` in your `config.h` file you can enable lighting layers. These make +it easy to use your underglow LEDs as status indicators to show which keyboard layer is currently active, or the state of caps lock, all without disrupting any animations. [Here's a video](https://youtu.be/uLGE1epbmdY) showing an example of what you can do. + +### Defining Lighting Layers {#defining-lighting-layers} + +By default, 8 layers are possible. This can be expanded to as many as 32 by overriding the definition of `RGBLIGHT_MAX_LAYERS` in `config.h` (e.g. `#define RGBLIGHT_MAX_LAYERS 32`). Please note, if you use a split keyboard, you will need to flash both sides of the split after changing this. Also, increasing the maximum will increase the firmware size, and will slow sync on split keyboards. + +To define a layer, we modify `keymap.c` to list the LED ranges and the colors we want to overlay on them using an array of `rgblight_segment_t` using the `RGBLIGHT_LAYER_SEGMENTS` macro. We can define multiple layers and enable/disable them independently: + +```c +// Light LEDs 6 to 9 and 12 to 15 red when caps lock is active. Hard to ignore! +const rgblight_segment_t PROGMEM my_capslock_layer[] = RGBLIGHT_LAYER_SEGMENTS( + {6, 4, HSV_RED}, // Light 4 LEDs, starting with LED 6 + {12, 4, HSV_RED} // Light 4 LEDs, starting with LED 12 +); +// Light LEDs 9 & 10 in cyan when keyboard layer 1 is active +const rgblight_segment_t PROGMEM my_layer1_layer[] = RGBLIGHT_LAYER_SEGMENTS( + {9, 2, HSV_CYAN} +); +// Light LEDs 11 & 12 in purple when keyboard layer 2 is active +const rgblight_segment_t PROGMEM my_layer2_layer[] = RGBLIGHT_LAYER_SEGMENTS( + {11, 2, HSV_PURPLE} +); +// Light LEDs 13 & 14 in green when keyboard layer 3 is active +const rgblight_segment_t PROGMEM my_layer3_layer[] = RGBLIGHT_LAYER_SEGMENTS( + {13, 2, HSV_GREEN} +); +// etc.. +``` + +We combine these layers into an array using the `RGBLIGHT_LAYERS_LIST` macro, and assign it to the `rgblight_layers` variable during keyboard setup. Note that you can only define up to 8 lighting layers. Any extra layers will be ignored. Since the different lighting layers overlap, the order matters in the array, with later layers taking precedence: + +```c +// Now define the array of layers. Later layers take precedence +const rgblight_segment_t* const PROGMEM my_rgb_layers[] = RGBLIGHT_LAYERS_LIST( + my_capslock_layer, + my_layer1_layer, // Overrides caps lock layer + my_layer2_layer, // Overrides other layers + my_layer3_layer // Overrides other layers +); + +void keyboard_post_init_user(void) { + // Enable the LED layers + rgblight_layers = my_rgb_layers; +} +``` +Note: For split keyboards with two controllers, both sides need to be flashed when updating the contents of rgblight_layers. + +### Enabling and disabling lighting layers {#enabling-lighting-layers} + +Everything above just configured the definition of each lighting layer. +We can now enable and disable the lighting layers whenever the state of the keyboard changes: + +```c +bool led_update_user(led_t led_state) { + rgblight_set_layer_state(0, led_state.caps_lock); + return true; +} + +layer_state_t default_layer_state_set_user(layer_state_t state) { + rgblight_set_layer_state(1, layer_state_cmp(state, _DVORAK)); + return state; +} + +layer_state_t layer_state_set_user(layer_state_t state) { + rgblight_set_layer_state(2, layer_state_cmp(state, _FN)); + rgblight_set_layer_state(3, layer_state_cmp(state, _ADJUST)); + return state; +} +``` + +### Lighting layer blink {#lighting-layer-blink} + +By including `#define RGBLIGHT_LAYER_BLINK` in your `config.h` file you can turn a lighting +layer on for a specified duration. Once the specified number of milliseconds has elapsed +the layer will be turned off. This is useful, e.g., if you want to acknowledge some +action (e.g. toggling some setting): + +```c +const rgblight_segment_t PROGMEM _yes_layer[] = RGBLIGHT_LAYER_SEGMENTS( {9, 6, HSV_GREEN} ); +const rgblight_segment_t PROGMEM _no_layer[] = RGBLIGHT_LAYER_SEGMENTS( {9, 6, HSV_RED} ); + +const rgblight_segment_t* const PROGMEM _rgb_layers[] = + RGBLIGHT_LAYERS_LIST( _yes_layer, _no_layer ); + +void keyboard_post_init_user(void) { + rgblight_layers = _rgb_layers; +} + +// Note we user post_process_record_user because we want the state +// after the flag has been flipped... +void post_process_record_user(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case QK_DEBUG_TOGGLE: + rgblight_blink_layer(debug_enable ? 0 : 1, 500); + break; + + case NK_TOGG: + case NK_ON: + case NK_OFF: + rgblight_blink_layer(keymap_config.nkro ? 0 : 1, 500); + break; + } +} +``` + +You can also use `rgblight_blink_layer_repeat` to specify the amount of times the layer is supposed to blink. Using the layers from above, +```c +void post_process_record_user(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case QK_DEBUG_TOGGLE: + rgblight_blink_layer_repeat(debug_enable ? 0 : 1, 200, 3); + break; + } +} +``` +would turn the layer 0 (or 1) on and off again three times when `DB_TOGG` is pressed. + +Blinking accumulates layers so if multiple layers are set blinking at the same time they will all blink for the duration and repeat times of the last layer to be blinked. +To stop these other layers from blinking use `rgblight_unblink_layer` or `rgblight_unblink_all_but_layer`: + +```c +rgblight_blink_layer(1, 500); +rgblight_unblink_all_but_layer(1); +``` + +```c +rgblight_unblink_layer(3); +rgblight_blink_layer(2, 500); +``` + +::: warning +Lighting layers on split keyboards will require layer state synced to the slave half (e.g. `#define SPLIT_LAYER_STATE_ENABLE`). See [data sync options](split_keyboard#data-sync-options) for more details. +::: + +### Overriding RGB Lighting on/off status + +Normally lighting layers are not shown when RGB Lighting is disabled (e.g. with `RGB_TOG` keycode). If you would like lighting layers to work even when the RGB Lighting is otherwise off, add `#define RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF` to your `config.h`. + +### Retain brightness + +Usually lighting layers apply their configured brightness once activated. If you would like lighting layers to retain the currently used brightness (as returned by `rgblight_get_val()`), add `#define RGBLIGHT_LAYERS_RETAIN_VAL` to your `config.h`. + +## 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/rgblight.h) for the full list, but the most commonly used functions include: + +### Low level Functions +|Function |Description | +|--------------------------------------------|-------------------------------------------| +|`rgblight_set()` |Flush out led buffers to LEDs | +|`rgblight_set_clipping_range(pos, num)` |Set clipping Range. see [Clipping Range](#clipping-range) | + +### Effects and Animations Functions +#### effect range setting +|Function |Description | +|--------------------------------------------|------------------| +|`rgblight_set_effect_range(pos, num)` |Set Effects Range | + +#### direct operation +|Function |Description | +|--------------------------------------------|-------------| +|`rgblight_setrgb_at(r, g, b, index)` |Set a single LED to the given RGB value, where `r`/`g`/`b` are between 0 and 255 and `index` is between 0 and `RGBLIGHT_LED_COUNT` (not written to EEPROM) | +|`rgblight_sethsv_at(h, s, v, index)` |Set a single LED to the given HSV value, where `h`/`s`/`v` are between 0 and 255, and `index` is between 0 and `RGBLIGHT_LED_COUNT` (not written to EEPROM) | +|`rgblight_setrgb_range(r, g, b, start, end)`|Set a continuous range of LEDs to the given RGB value, where `r`/`g`/`b` are between 0 and 255 and `start`(included) and `stop`(excluded) are between 0 and `RGBLIGHT_LED_COUNT` (not written to EEPROM)| +|`rgblight_sethsv_range(h, s, v, start, end)`|Set a continuous range of LEDs to the given HSV value, where `h`/`s`/`v` are between 0 and 255, and `start`(included) and `stop`(excluded) are between 0 and `RGBLIGHT_LED_COUNT` (not written to EEPROM)| +|`rgblight_setrgb(r, g, b)` |Set effect range LEDs to the given RGB value where `r`/`g`/`b` are between 0 and 255 (not written to EEPROM) | +|`rgblight_setrgb_master(r, g, b)` |Set the LEDs on the master side to the given RGB value, where `r`/`g`/`b` are between 0 and 255 (not written to EEPROM) | +|`rgblight_setrgb_slave(r, g, b)` |Set the LEDs on the slave side to the given RGB value, where `r`/`g`/`b` are between 0 and 255 (not written to EEPROM) | +|`rgblight_sethsv_master(h, s, v)` |Set the LEDs on the master side to the given HSV value, where `h`/`s`/`v` are between 0 and 255 (not written to EEPROM) | +|`rgblight_sethsv_slave(h, s, v)` |Set the LEDs on the slave side to the given HSV value, where `h`/`s`/`v` are between 0 and 255 (not written to EEPROM) | + +Example: +```c +rgblight_sethsv_at(HSV_WHITE, 0); // led 0 +rgblight_sethsv_at(HSV_RED, 1); // led 1 +rgblight_sethsv_at(HSV_GREEN, 2); // led 2 +// The above functions automatically calls rgblight_set(), so there is no need to call it explicitly. +// Note that it is inefficient to call repeatedly. +``` + +#### effect mode change +|Function |Description | +|--------------------------------------------|-------------| +|`rgblight_mode(x)` |Set the mode, if RGB animations are enabled | +|`rgblight_mode_noeeprom(x)` |Set the mode, if RGB animations are enabled (not written to EEPROM) | +|`rgblight_step()` |Change the mode to the next RGB animation in the list of enabled RGB animations | +|`rgblight_step_noeeprom()` |Change the mode to the next RGB animation in the list of enabled RGB animations (not written to EEPROM) | +|`rgblight_step_reverse()` |Change the mode to the previous RGB animation in the list of enabled RGB animations | +|`rgblight_step_reverse_noeeprom()` |Change the mode to the previous RGB animation in the list of enabled RGB animations (not written to EEPROM) | +|`rgblight_reload_from_eeprom()` |Reload the effect configuration (enabled, mode and color) from EEPROM | + +#### effects mode disable/enable +|Function |Description | +|--------------------------------------------|-------------| +|`rgblight_toggle()` |Toggle effect range LEDs between on and off | +|`rgblight_toggle_noeeprom()` |Toggle effect range LEDs between on and off (not written to EEPROM) | +|`rgblight_enable()` |Turn effect range LEDs on, based on their previous state | +|`rgblight_enable_noeeprom()` |Turn effect range LEDs on, based on their previous state (not written to EEPROM) | +|`rgblight_disable()` |Turn effect range LEDs off | +|`rgblight_disable_noeeprom()` |Turn effect range LEDs off (not written to EEPROM) | + +#### hue, sat, val change +|Function |Description | +|--------------------------------------------|-------------| +|`rgblight_increase_hue()` |Increase the hue for effect range LEDs. This wraps around at maximum hue | +|`rgblight_increase_hue_noeeprom()` |Increase the hue for effect range LEDs. This wraps around at maximum hue (not written to EEPROM) | +|`rgblight_decrease_hue()` |Decrease the hue for effect range LEDs. This wraps around at minimum hue | +|`rgblight_decrease_hue_noeeprom()` |Decrease the hue for effect range LEDs. This wraps around at minimum hue (not written to EEPROM) | +|`rgblight_increase_sat()` |Increase the saturation for effect range LEDs. This stops at maximum saturation | +|`rgblight_increase_sat_noeeprom()` |Increase the saturation for effect range LEDs. This stops at maximum saturation (not written to EEPROM) | +|`rgblight_decrease_sat()` |Decrease the saturation for effect range LEDs. This stops at minimum saturation | +|`rgblight_decrease_sat_noeeprom()` |Decrease the saturation for effect range LEDs. This stops at minimum saturation (not written to EEPROM) | +|`rgblight_increase_val()` |Increase the value for effect range LEDs. This stops at maximum value | +|`rgblight_increase_val_noeeprom()` |Increase the value for effect range LEDs. This stops at maximum value (not written to EEPROM) | +|`rgblight_decrease_val()` |Decrease the value for effect range LEDs. This stops at minimum value | +|`rgblight_decrease_val_noeeprom()` |Decrease the value for effect range LEDs. This stops at minimum value (not written to EEPROM) | +|`rgblight_sethsv(h, s, v)` |Set effect range LEDs to the given HSV value where `h`/`s`/`v` are between 0 and 255 | +|`rgblight_sethsv_noeeprom(h, s, v)` |Set effect range LEDs to the given HSV value where `h`/`s`/`v` are between 0 and 255 (not written to EEPROM) | + +#### Speed functions +|Function |Description | +|--------------------------------------------|-------------| +|`rgblight_increase_speed()` |Increases the animation speed | +|`rgblight_increase_speed_noeeprom()` |Increases the animation speed (not written to EEPROM) | +|`rgblight_decrease_speed()` |Decreases the animation speed | +|`rgblight_decrease_speed_noeeprom()` |Decreases the animation speed (not written to EEPROM) | +|`rgblight_set_speed()` |Sets the speed. Value is between 0 and 255 | +|`rgblight_set_speed_noeeprom()` |Sets the speed. Value is between 0 and 255 (not written to EEPROM) | + + +#### layer functions +|Function |Description | +|--------------------------------------------|-------------| +|`rgblight_get_layer_state(i)` |Returns `true` if lighting layer `i` is enabled | +|`rgblight_set_layer_state(i, is_on)` |Enable or disable lighting layer `i` based on value of `bool is_on` | + +#### query +|Function |Description | +|-----------------------|---------------------------| +|`rgblight_is_enabled()`|Gets current on/off status | +|`rgblight_get_mode()` |Gets current mode | +|`rgblight_get_hue()` |Gets current hue | +|`rgblight_get_sat()` |Gets current sat | +|`rgblight_get_val()` |Gets current val | +|`rgblight_get_speed()` |Gets current speed | + +## Colors + +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_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); +rgblight_sethsv_noeeprom(HSV_GREEN); +rgblight_setrgb_at(RGB_GOLD, 3); +rgblight_sethsv_range(HSV_WHITE, 0, 6); +``` + +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 + +If you want to make the logical order of LEDs different from the electrical connection order, you can do this by defining the `RGBLIGHT_LED_MAP` macro in your `config.h`. + +Normally, the contents of the LED buffer are output to the LEDs in the same order. +simple dicrect + +By defining `RGBLIGHT_LED_MAP` as in the example below, you can specify the LED with addressing in reverse order of the electrical connection order. + +```c +// config.h + +#define RGBLIGHT_LED_COUNT 4 +#define RGBLIGHT_LED_MAP { 3, 2, 1, 0 } + +``` +simple mapped + +## Clipping Range + +Using the `rgblight_set_clipping_range()` function, you can prepare more buffers than the actual number of LEDs, and output some of the buffers to the LEDs. This is useful if you want the split keyboard to treat left and right LEDs as logically contiguous. + +You can set the Clipping Range by executing the following code. + +```c +// some source +rgblight_set_clipping_range(3, 4); +``` +clip direct + +In addition to setting the Clipping Range, you can use `RGBLIGHT_LED_MAP` together. + +```c +// config.h +#define RGBLIGHT_LED_COUNT 8 +#define RGBLIGHT_LED_MAP { 7, 6, 5, 4, 3, 2, 1, 0 } + +// some source +rgblight_set_clipping_range(3, 4); +``` +clip mapped + +## Hardware Modification + +If your keyboard lacks onboard underglow LEDs, you may often be able to solder on an RGB LED strip yourself. You will need to find an unused pin to wire to the data pin of your LED strip. Some keyboards may break out unused pins from the MCU to make soldering easier. The other two pins, VCC and GND, must also be connected to the appropriate power pins. + +## Velocikey + +Velocikey is a feature that lets you control the speed of lighting effects (like the Rainbow Swirl effect) with the speed of your typing. The faster you type, the faster the lights will go! + +### Usage +For Velocikey to take effect, there are two steps. First, when compiling your keyboard, you'll need to set `VELOCIKEY_ENABLE=yes` in `rules.mk`, e.g.: + +``` +MOUSEKEY_ENABLE = no +STENO_ENABLE = no +EXTRAKEY_ENABLE = yes +VELOCIKEY_ENABLE = yes +``` + +Then, while using your keyboard, you need to also turn it on with the `VK_TOGG` keycode, which toggles the feature on and off. + +The following light effects will all be controlled by Velocikey when it is enabled: + - RGB Breathing + - RGB Rainbow Mood + - RGB Rainbow Swirl + - RGB Snake + - RGB Knight + +Support for LED breathing effects is planned but not available yet. + + As long as Velocikey is enabled, it will control the speed regardless of any other speed setting that your RGB lights are currently on. + + ### Configuration + Velocikey doesn't currently support any configuration via keyboard settings. If you want to adjust something like the speed increase or decay rate, you would need to edit `velocikey.c` and adjust the values there to achieve the kinds of speeds that you like. diff --git a/docs/features/secure.md b/docs/features/secure.md new file mode 100644 index 0000000000..02ed20a470 --- /dev/null +++ b/docs/features/secure.md @@ -0,0 +1,56 @@ +# Secure + +The secure feature aims to prevent unwanted interaction without user intervention. + +::: tip +Secure does **not** currently implement encryption/decryption/etc and should not be a replacement where a strong hardware/software based solution is required. +::: + +### Unlock sequence + +To unlock, the user must perform a set of actions. This can optionally be configured to be multiple keys. + +* While unlocking all keyboard input is ignored +* Incorrect attempts will revert back to the previously locked state + +### Automatic Locking + +Once unlocked, the keyboard will revert back to a locked state after the configured timeout. +The timeout can be refreshed by using the `secure_activity_event` function, for example from one of the various [hooks](../custom_quantum_functions). + +## Usage + +Add the following to your `rules.mk`: + +```make +SECURE_ENABLE = yes +``` + +## Keycodes + +| Key |Aliases | Description | +|---------------------|---------|--------------------------------------------------------------------------------| +| `QK_SECURE_LOCK` |`SE_LOCK`| Revert back to a locked state | +| `QK_SECURE_UNLOCK` |`SE_UNLK`| Forces unlock without performing a unlock sequence | +| `QK_SECURE_TOGGLE` |`SE_TOGG`| Toggle directly between locked and unlock without performing a unlock sequence | +| `QK_SECURE_REQUEST` |`SE_REQ` | Request that user perform the unlock sequence | + +## Configuration + +| Define | Default | Description | +|-------------------------|----------------|---------------------------------------------------------------------------------| +|`SECURE_UNLOCK_TIMEOUT` | `5000` | Timeout for the user to perform the configured unlock sequence - `0` to disable | +|`SECURE_IDLE_TIMEOUT` | `60000` | Timeout while unlocked before returning to locked - `0` to disable | +|`SECURE_UNLOCK_SEQUENCE` | `{ { 0, 0 } }` | Array of matrix locations describing a sequential sequence of keypresses | + +## Functions + +| Function | Description | +|---------------------------|----------------------------------------------------------------------------| +| `secure_is_locked()` | Check if the device is currently locked | +| `secure_is_unlocking()` | Check if an unlock sequence is currently in progress | +| `secure_is_unlocked()` | Check if the device is currently unlocked | +| `secure_lock()` | Lock down the device | +| `secure_unlock()` | Force unlock the device - bypasses user unlock sequence | +| `secure_request_unlock()` | Begin listening for an unlock sequence | +| `secure_activity_event()` | Flag that user activity has happened and the device should remain unlocked | diff --git a/docs/features/send_string.md b/docs/features/send_string.md new file mode 100644 index 0000000000..ed9311e20a --- /dev/null +++ b/docs/features/send_string.md @@ -0,0 +1,226 @@ +# Send String {#send-string} + +The Send String API is part of QMK's macro system. It allows for sequences of keystrokes to be sent automatically. + +The full ASCII character set is supported, along with all of the keycodes in the Basic Keycode range (as these are the only ones that will actually be sent to the host). + +::: tip +Unicode characters are **not** supported with this API -- see the [Unicode](unicode) feature instead. +::: + +## Usage {#usage} + +Send String is enabled by default, so there is usually no need for any special setup. However, if it is disabled, add the following to your `rules.mk`: + +```make +SEND_STRING_ENABLE = yes +``` + +## Basic Configuration {#basic-configuration} + +Add the following to your `config.h`: + +|Define |Default |Description | +|-----------------|----------------|------------------------------------------------------------------------------------------------------------| +|`SENDSTRING_BELL`|*Not defined* |If the [Audio](audio) feature is enabled, the `\a` character (ASCII `BEL`) will beep the speaker.| +|`BELL_SOUND` |`TERMINAL_SOUND`|The song to play when the `\a` character is encountered. By default, this is an eighth note of C5. | + +## Keycodes {#keycodes} + +The Send String functions accept C string literals, but specific keycodes can be injected with the below macros. All of the keycodes in the [Basic Keycode range](../keycodes_basic) are supported (as these are the only ones that will actually be sent to the host), but with an `X_` prefix instead of `KC_`. + +|Macro |Description | +|--------------|-------------------------------------------------------------------| +|`SS_TAP(x)` |Send a keydown, then keyup, event for the given Send String keycode| +|`SS_DOWN(x)` |Send a keydown event for the given Send String keycode | +|`SS_UP(x)` |Send a keyup event for the given Send String keycode | +|`SS_DELAY(ms)`|Wait for `ms` milliseconds | + +The following characters are also mapped to their respective keycodes for convenience: + +|Character|Hex |ASCII|Keycode | +|---------|------|-----|--------------| +|`\b` |`\x08`|`BS` |`KC_BACKSPACE`| +|`\e` |`\x09`|`ESC`|`KC_ESCAPE` | +|`\n` |`\x0A`|`LF` |`KC_ENTER` | +|`\t` |`\x1B`|`TAB`|`KC_TAB` | +| |`\x7F`|`DEL`|`KC_DELETE` | + +### Language Support {#language-support} + +By default, Send String assumes your OS keyboard layout is set to US ANSI. If you are using a different keyboard layout, you can [override the lookup tables used to convert ASCII characters to keystrokes](../reference_keymap_extras#sendstring-support). + +## Examples {#examples} + +### Hello World {#example-hello-world} + +A simple custom keycode which types out "Hello, world!" and the Enter key when pressed. + +Add the following to your `keymap.c`: + +```c +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case SS_HELLO: + if (record->event.pressed) { + SEND_STRING("Hello, world!\n"); + } + return false; + } + + return true; +} +``` + +### Keycode Injection {#example-keycode-injection} + +This example types out opening and closing curly braces, then taps the left arrow key to move the cursor between the two. + +```c +SEND_STRING("{}" SS_TAP(X_LEFT)); +``` + +This example types Ctrl+A, then Ctrl+C, without releasing Ctrl. + +```c +SEND_STRING(SS_LCTL("ac")); +``` + +## API {#api} + +### `void send_string(const char *string)` {#api-send-string} + +Type out a string of ASCII characters. + +This function simply calls `send_string_with_delay(string, 0)`. + +#### Arguments {#api-send-string-arguments} + + - `const char *string` + The string to type out. + +--- + +### `void send_string_with_delay(const char *string, uint8_t interval)` {#api-send-string-with-delay} + +Type out a string of ASCII characters, with a delay between each character. + +#### Arguments {#api-send-string-with-delay-arguments} + + - `const char *string` + The string to type out. + - `uint8_t interval` + The amount of time, in milliseconds, to wait before typing the next character. + +--- + +### `void send_string_P(const char *string)` {#api-send-string-p} + +Type out a PROGMEM string of ASCII characters. + +On ARM devices, this function is simply an alias for `send_string_with_delay(string, 0)`. + +#### Arguments {#api-send-string-p-arguments} + + - `const char *string` + The string to type out. + +--- + +### `void send_string_with_delay_P(const char *string, uint8_t interval)` {#api-send-string-with-delay-p} + +Type out a PROGMEM string of ASCII characters, with a delay between each character. + +On ARM devices, this function is simply an alias for `send_string_with_delay(string, interval)`. + +#### Arguments {#api-send-string-with-delay-p-arguments} + + - `const char *string` + The string to type out. + - `uint8_t interval` + The amount of time, in milliseconds, to wait before typing the next character. + +--- + +### `void send_char(char ascii_code)` {#api-send-char} + +Type out an ASCII character. + +#### Arguments {#api-send-char-arguments} + + - `char ascii_code` + The character to type. + +--- + +### `void send_dword(uint32_t number)` {#api-send-dword} + +Type out an eight digit (unsigned 32-bit) hexadecimal value. + +The format is `[0-9a-f]{8}`, eg. `00000000` through `ffffffff`. + +#### Arguments {#api-send-dword-arguments} + + - `uint32_t number` + The value to type, from 0 to 4,294,967,295. + +--- + +### `void send_word(uint16_t number)` {#api-send-word} + +Type out a four digit (unsigned 16-bit) hexadecimal value. + +The format is `[0-9a-f]{4}`, eg. `0000` through `ffff`. + +#### Arguments {#api-send-word-arguments} + + - `uint16_t number` + The value to type, from 0 to 65,535. + +--- + +### `void send_byte(uint8_t number)` {#api-send-bytes} + +Type out a two digit (8-bit) hexadecimal value. + +The format is `[0-9a-f]{2}`, eg. `00` through `ff`. + +#### Arguments {#api-send-byte-arguments} + + - `uint8_t number` + The value to type, from 0 to 255. + +--- + +### `void send_nibble(uint8_t number)` {#api-send-nibble} + +Type out a single hexadecimal digit. + +The format is `[0-9a-f]{1}`, eg. `0` through `f`. + +#### Arguments {#api-send-nibble-arguments} + + - `uint8_t number` + The value to type, from 0 to 15. + +--- + +### `void tap_random_base64(void)` {#api-tap-random-base64} + +Type a pseudorandom character from the set `A-Z`, `a-z`, `0-9`, `+` and `/`. + +--- + +### `SEND_STRING(string)` {#api-send-string-macro} + +Shortcut macro for `send_string_with_delay_P(PSTR(string), 0)`. + +On ARM devices, this define evaluates to `send_string_with_delay(string, 0)`. + +--- + +### `SEND_STRING_DELAY(string, interval)` {#api-send-string-delay-macro} + +Shortcut macro for `send_string_with_delay_P(PSTR(string), interval)`. + +On ARM devices, this define evaluates to `send_string_with_delay(string, interval)`. diff --git a/docs/features/sequencer.md b/docs/features/sequencer.md new file mode 100644 index 0000000000..f5f1f549af --- /dev/null +++ b/docs/features/sequencer.md @@ -0,0 +1,89 @@ +# 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)! + +::: warning +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 + +|Key |Aliases |Description | +|-------------------------------|---------|---------------------------------------------------| +|`QK_SEQUENCER_ON` |`SQ_ON` |Start the step sequencer | +|`QK_SEQUENCER_OFF` |`SQ_OFF` |Stop the step sequencer | +|`QK_SEQUENCER_TOGGLE` |`SQ_TOGG`|Toggle the step sequencer playback | +|`QK_SEQUENCER_STEPS_ALL` |`SQ_SALL`|Enable all the steps | +|`QK_SEQUENCER_STEPS_CLEAR` |`SQ_SCLR`|Disable all the steps | +|`QK_SEQUENCER_TEMPO_DOWN` |`SQ_TMPD`|Decrease the tempo | +|`QK_SEQUENCER_TEMPO_UP` |`SQ_TMPU`|Increase the tempo | +|`QK_SEQUENCER_RESOLUTION_DOWN` |`SQ_RESD`|Change to the slower resolution | +|`QK_SEQUENCER_RESOLUTION_UP` |`SQ_RESU`|Change to the faster resolution | +|`SQ_S(n)` | |Toggle the step `n` | +|`SQ_R(n)` | |Set the resolution to n | +|`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/features/space_cadet.md b/docs/features/space_cadet.md new file mode 100644 index 0000000000..0abdaebf33 --- /dev/null +++ b/docs/features/space_cadet.md @@ -0,0 +1,60 @@ +# Space Cadet: The Future, Built In + +Steve Losh described the [Space Cadet Shift](https://web.archive.org/web/20230330090938/https://stevelosh.com/blog/2012/10/a-modern-space-cadet/) quite well. Essentially, when you tap Left Shift on its own, you get an opening parenthesis; tap Right Shift on its own and you get the closing one. When held, the Shift keys function as normal. Yes, it's as cool as it sounds, and now even cooler supporting Control and Alt as well! + +## Usage + +Firstly, in your keymap, do one of the following: +- Replace the Left Shift key with `SC_LSPO` (Left Shift, Parenthesis Open), and Right Shift with `SC_RSPC` (Right Shift, Parenthesis Close). +- Replace the Left Control key with `SC_LCPO` (Left Control, Parenthesis Open), and Right Control with `SC_RCPC` (Right Control, Parenthesis Close). +- Replace the Left Alt key with `SC_LAPO` (Left Alt, Parenthesis Open), and Right Alt with `SC_RAPC` (Right Alt, Parenthesis Close). +- Replace any Shift key in your keymap with `SC_SENT` (Right Shift, Enter). + +## Keycodes + +|Keycode |Aliases |Description | +|----------------------------------------------|---------|----------------------------------------| +|`QK_SPACE_CADET_LEFT_CTRL_PARENTHESIS_OPEN` |`SC_LCPO`|Left Control when held, `(` when tapped | +|`QK_SPACE_CADET_RIGHT_CTRL_PARENTHESIS_CLOSE` |`SC_RCPC`|Right Control when held, `)` when tapped| +|`QK_SPACE_CADET_LEFT_SHIFT_PARENTHESIS_OPEN` |`SC_LSPO`|Left Shift when held, `(` when tapped | +|`QK_SPACE_CADET_RIGHT_SHIFT_PARENTHESIS_CLOSE`|`SC_RSPC`|Right Shift when held, `)` when tapped | +|`QK_SPACE_CADET_LEFT_ALT_PARENTHESIS_OPEN` |`SC_LAPO`|Left Alt when held, `(` when tapped | +|`QK_SPACE_CADET_RIGHT_ALT_PARENTHESIS_CLOSE` |`SC_RAPC`|Right Alt when held, `)` when tapped | +|`QK_SPACE_CADET_RIGHT_SHIFT_ENTER` |`SC_SENT`|Right Shift when held, Enter when tapped| + +## Caveats + +Space Cadet's functionality can conflict with the default Command functionality when both Shift keys are held at the same time. See the [Command feature](command) for info on how to change it, or make sure that Command is disabled in your `rules.mk` with: + +```make +COMMAND_ENABLE = no +``` + +## Configuration + +By default Space Cadet assumes a US ANSI layout, but if your layout uses different keys for parentheses, you can redefine them in your `config.h`. In addition, you can redefine the modifier to send on tap, or even send no modifier at all. The new configuration defines bundle all options up into a single define of 3 key codes in this order: the `Modifier` when held or when used with other keys, the `Tap Modifer` sent when tapped (no modifier if `KC_TRNS`), finally the `Keycode` sent when tapped. Now keep in mind, mods from other keys will still apply to the `Keycode` if say `KC_RSFT` is held while tapping `SC_LSPO` key with `KC_TRNS` as the `Tap Modifer`. + +|Define |Default |Description | +|----------------|-------------------------------|---------------------------------------------------------------------------------| +|`LSPO_KEYS` |`KC_LSFT, LSPO_MOD, LSPO_KEY` |Send `KC_LSFT` when held, the mod and key defined by `LSPO_MOD` and `LSPO_KEY`. | +|`RSPC_KEYS` |`KC_RSFT, RSPC_MOD, RSPC_KEY` |Send `KC_RSFT` when held, the mod and key defined by `RSPC_MOD` and `RSPC_KEY`. | +|`LCPO_KEYS` |`KC_LCTL, KC_LSFT, KC_9` |Send `KC_LCTL` when held, the mod `KC_LSFT` with the key `KC_9` when tapped. | +|`RCPC_KEYS` |`KC_RCTL, KC_RSFT, KC_0` |Send `KC_RCTL` when held, the mod `KC_RSFT` with the key `KC_0` when tapped. | +|`LAPO_KEYS` |`KC_LALT, KC_LSFT, KC_9` |Send `KC_LALT` when held, the mod `KC_LSFT` with the key `KC_9` when tapped. | +|`RAPC_KEYS` |`KC_RALT, KC_RSFT, KC_0` |Send `KC_RALT` when held, the mod `KC_RSFT` with the key `KC_0` when tapped. | +|`SFTENT_KEYS` |`KC_RSFT, KC_TRNS, SFTENT_KEY` |Send `KC_RSFT` when held, no mod with the key `SFTENT_KEY` when tapped. | +|`SPACE_CADET_MODIFIER_CARRYOVER` |*Not defined* |Store current modifiers before the hold mod is pressed and use them with the tap mod and keycode. Useful for when you frequently release a modifier before triggering Space Cadet. | + + +## Obsolete Configuration + +These defines are used in the above defines internally to support backwards compatibility, so you may continue to use them, however the above defines open up a larger range of flexibility than before. As an example, say you want to not send any modifier when you tap just `SC_LSPO`, with the old defines you had an all or nothing choice of using the `DISABLE_SPACE_CADET_MODIFIER` define. Now you can define that key as: `#define LSPO_KEYS KC_LSFT, KC_TRNS, KC_9`. This tells the system to set Left Shift if held or used with other keys, then on tap send no modifier (transparent) with the `KC_9`. + +|Define |Default |Description | +|------------------------------|-------------|------------------------------------------------------------------| +|`LSPO_KEY` |`KC_9` |The keycode to send when Left Shift is tapped | +|`RSPC_KEY` |`KC_0` |The keycode to send when Right Shift is tapped | +|`LSPO_MOD` |`KC_LSFT` |The modifier to apply to `LSPO_KEY` | +|`RSPC_MOD` |`KC_RSFT` |The modifier to apply to `RSPC_KEY` | +|`SFTENT_KEY` |`KC_ENT` |The keycode to send when the Shift key is tapped | +|`DISABLE_SPACE_CADET_MODIFIER`|*Not defined*|If defined, prevent the Space Cadet from applying a modifier | diff --git a/docs/features/split_keyboard.md b/docs/features/split_keyboard.md new file mode 100644 index 0000000000..6efa1c2a35 --- /dev/null +++ b/docs/features/split_keyboard.md @@ -0,0 +1,508 @@ +# Split Keyboard + +Many keyboards in the QMK Firmware repo are "split" keyboards. They use two controllers—one plugging into USB, and the second connected by a serial or an I2C connection over a TRRS or similar cable. + +Split keyboards can have a lot of benefits, but there is some additional work needed to get them enabled. + +QMK Firmware has a generic implementation that is usable by any board, as well as numerous board specific implementations. + +For this, we will mostly be talking about the generic implementation used by the Let's Split and other keyboards. + +::: warning +ARM split supports most QMK subsystems when using the 'serial' and 'serial_usart' drivers. I2C slave is currently unsupported. +::: + +::: warning +Both sides must use the same MCU family, for eg two Pro Micro-compatible controllers or two Blackpills. Currently, mixing AVR and ARM is not possible as ARM vs AVR uses different method for serial communication, and are not compatible. Moreover Blackpill's uses 3.3v logic, and atmega32u4 uses 5v logic. +::: + +## Compatibility Overview + +| Transport | AVR | ARM | +|------------------------------|--------------------|--------------------| +| ['serial'](../drivers/serial) | :heavy_check_mark: | :white_check_mark: 1 | +| I2C | :heavy_check_mark: | | + +Notes: + +1. Both hardware and software limitations are detailed within the [driver documentation](../drivers/serial). + +## Hardware Configuration + +This assumes that you're using two Pro Micro-compatible controllers, and are using TRRS jacks to connect to two halves. + +### Required Hardware + +Apart from diodes and key switches for the keyboard matrix in each half, you will need 2x TRRS sockets and 1x TRRS cable. + +Alternatively, you can use any sort of cable and socket that has at least 3 wires. + +If you want to use I2C to communicate between halves, you will need a cable with at least 4 wires and 2x 4.7kΩ pull-up resistors. + +#### Considerations + +The most commonly used connection is a TRRS cable and jacks. These provide 4 wires, making them very useful for split keyboards, and are easy to find. + +However, since one of the wires carries VCC, this means that the boards are not hot pluggable. You should always disconnect the board from USB before unplugging and plugging in TRRS cables, or you can short the controller, or worse. + +Another option is to use phone cables (as in, old school RJ-11/RJ-14 cables). Make sure that you use one that actually supports 4 wires/lanes. + +However, USB cables, SATA cables, and even just 4 wires have been known to be used for communication between the controllers. + +::: warning +Using USB cables for communication between the controllers works just fine, but the connector could be mistaken for a normal USB connection and potentially short out the keyboard, depending on how it's wired. For this reason, they are not recommended for connecting split keyboards. +::: + +### Serial Wiring + +The 3 wires of the TRS/TRRS cable need to connect GND, VCC, and D0/D1/D2/D3 (aka PD0/PD1/PD2/PD3) between the two Pro Micros. + +::: tip +Note that the pin used here is actually set by `SOFT_SERIAL_PIN` below. +::: + +sk-pd0-connection-mono +sk-pd2-connection-mono + +### I2C Wiring + +The 4 wires of the TRRS cable need to connect GND, VCC, and SCL and SDA (aka PD0/pin 3 and PD1/pin 2, respectively) between the two Pro Micros. + +The pull-up resistors may be placed on either half. If you wish to use the halves independently, it is also possible to use 4 resistors and have the pull-ups in both halves. +Note that the total resistance for the connected system should be within spec at 2.2k-10kOhm, with an 'ideal' at 4.7kOhm, regardless of the placement and number. + +sk-i2c-connection-mono + +## Firmware Configuration + +To enable the split keyboard feature, add the following to your `rules.mk`: + +```make +SPLIT_KEYBOARD = yes +``` + +If you're using a custom transport (communication method), then you will also need to add: + +```make +SPLIT_TRANSPORT = custom +``` + +### Layout Macro + +Configuring your layout in a split keyboard works slightly differently to a non-split keyboard. Take for example the following layout. The top left numbers refer to the matrix row and column, and the bottom right are the order of the keys in the layout: + +![Physical layout](https://i.imgur.com/QeY6kMQ.png) + +Since the matrix scanning procedure operates on entire rows, it first populates the left half's rows, then the right half's. Thus, the matrix as QMK views it has double the rows instead of double the columns: + +![Matrix](https://i.imgur.com/4wjJzBU.png) + +### Setting Handedness + +By default, the firmware does not know which side is which; it needs some help to determine that. There are several ways to do this, listed in order of precedence. + +#### Handedness by Pin + +You can configure the firmware to read a pin on the controller to determine handedness. To do this, add the following to your `config.h` file: + +```c +#define SPLIT_HAND_PIN B7 +``` + +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 + +You can configure the firmware to read key matrix pins on the controller to determine handedness. To do this, add the following to your `config.h` file: + +```c +#define SPLIT_HAND_MATRIX_GRID D0, F1 +``` + +The first pin is the output pin and the second is the input pin. + +Some keyboards have unused intersections in the key matrix. This setting uses one of these unused intersections to determine the handedness. + +Normally, when a diode is connected to an intersection, it is judged to be right. If you add the following definition, it will be judged to be left. + +```c +#define SPLIT_HAND_MATRIX_GRID_LOW_IS_LEFT +``` + +Note that adding a diode at a previously unused intersection will effectively tell the firmware that there is a key held down at that point. You can instruct qmk to ignore that intersection by defining `MATRIX_MASKED` and then defining a `matrix_row_t matrix_mask[MATRIX_ROWS]` array in your keyboard config. Each bit of a single value (starting form the least-significant bit) is used to tell qmk whether or not to pay attention to key presses at that intersection. + +While `MATRIX_MASKED` isn't necessary to use `SPLIT_HAND_MATRIX_GRID` successfully, without it you may experience issues trying to suspend your computer with your keyboard attached as the matrix will always report at least one key-press. + +#### Handedness by EEPROM + +This method sets the keyboard's handedness by setting a flag in the persistent storage (`EEPROM`). This is checked when the controller first starts up, and determines what half the keyboard is, and how to orient the keyboard layout. + + +To enable this method, add the following to your `config.h` file: + +```c +#define EE_HANDS +``` + +Next, you will have to flash the correct handedness option to the controller on each halve. You can do this manually with the following bootloader targets using `qmk flash -kb -km -bl ` command to flash: + +|Microcontroller Type|Bootloader Parameter| +|--------------------|--------------------| +|AVR controllers with Caterina bootloader
(e.g. Pro Micro)|`avrdude-split-left`
`avrdude-split-right`| +|AVR controllers with the stock Amtel DFU or DFU compatible bootloader
(e.g. Elite-C)|`dfu-split-left`
`dfu-split-right`| +|ARM controllers with a DFU compatible bootloader
(e.g. Proton-C)|`dfu-util-split-left`
`dfu-util-split-right`| +|ARM controllers with a UF2 compatible bootloader
(e.g. RP2040)|`uf2-split-left`
`uf2-split-right`| + +Example for `crkbd/rev1` keyboard with normal AVR Pro Micro MCUs, reset the left controller and run: +``` +qmk flash -kb crkbd/rev1 -km default -bl avrdude-split-left +``` +Reset the right controller and run: +``` +qmk flash -kb crkbd/rev1 -km default -bl avrdude-split-right +``` + +::: tip +Some controllers (e.g. Blackpill with DFU compatible bootloader) will need to be flashed with handedness bootloader parameter every time because it is not retained between flashes. +::: + +::: tip +[QMK Toolbox](https://github.com/qmk/qmk_toolbox/releases/) can also be used to flash EEPROM handedness files. Place the controller in bootloader mode and select menu option Tools -> EEPROM -> Set Left/Right Hand +::: + +This setting is not changed when re-initializing the EEPROM using the `EE_CLR` key, or using the `eeconfig_init()` function. However, if you reset the EEPROM outside of the firmware's built in options (such as flashing a file that overwrites the `EEPROM`, like how the [QMK Toolbox](https://github.com/qmk/qmk_toolbox/releases/)'s "Reset EEPROM" button works), you'll need to re-flash the controller with the `EEPROM` files. + +You can find the `EEPROM` files in the QMK firmware repo, [here](https://github.com/qmk/qmk_firmware/tree/master/quantum/split_common). + + +#### Handedness by `#define` + +You can use this option when USB cable is always connected to just one side of the split keyboard. + +If the USB cable is always connected to the right side, add the following to your `config.h` file and flash both sides with this option: +```c +#define MASTER_RIGHT +``` + +If the USB cable is always connected to the left side, add the following to your `config.h` file and flash both sides with this option: +```c +#define MASTER_LEFT +``` + +::: tip +If neither options are defined, the handedness defaults to `MASTER_LEFT`. +::: + + +### Communication Options + +Because not every split keyboard is identical, there are a number of additional options that can be configured in your `config.h` file. + +```c +#define USE_I2C +``` + +This configures the use of I2C support for split keyboard transport (AVR only). + +```c +#define SOFT_SERIAL_PIN D0 +``` + +This sets the pin to be used for serial communication. If you're not using serial, you shouldn't need to define this. + +However, if you are using serial and I2C on the board, you will need to set this, and to something other than D0 and D1 (as these are used for I2C communication). + +```c +#define SELECT_SOFT_SERIAL_SPEED {#} +``` + +If you're having issues with serial communication, you can change this value, as it controls the communication speed for serial. The default is 1, and the possible values are: + +* **`0`**: about 189kbps (Experimental only) +* **`1`**: about 137kbps (default) +* **`2`**: about 75kbps +* **`3`**: about 39kbps +* **`4`**: about 26kbps +* **`5`**: about 20kbps + +```c +#define FORCED_SYNC_THROTTLE_MS 100 +``` + +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_MAX_CONNECTION_ERRORS 10 +``` +This sets the maximum number of failed communication attempts (one per scan cycle) from the master part before it assumes that no slave part is connected. This makes it possible to use a master part without the slave part connected. + +Set to 0 to disable the disconnection check altogether. + +```c +#define SPLIT_CONNECTION_CHECK_TIMEOUT 500 +``` +How long (in milliseconds) the master part should block all connection attempts to the slave after the communication has been flagged as disconnected (see `SPLIT_MAX_CONNECTION_ERRORS` above). + +One communication attempt will be allowed everytime this amount of time has passed since the last attempt. If that attempt succeeds, the communication is seen as working again. + +Set to 0 to disable this throttling of communications while disconnected. This can save you a couple of bytes of firmware size. + + +### Data Sync Options + +The following sync options add overhead to the split communication protocol and may negatively impact the matrix scan speed when enabled. These can be enabled by adding the chosen option(s) to your `config.h` file. + +```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. The purpose of this feature is to support cosmetic use of key events (e.g. RGB reacting to keypresses). + +```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. + +```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. + +```c +#define SPLIT_MODS_ENABLE +``` + +This enables transmitting modifier state (normal, weak, oneshot and oneshot locked) 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). + +```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). + +```c +#define SPLIT_OLED_ENABLE +``` + +This enables transmitting the current OLED on/off status to the slave side of the split keyboard. The purpose of this feature is to support state (on/off state only) syncing. + +```c +#define SPLIT_ST7565_ENABLE +``` + +This enables transmitting the current ST7565 on/off status to the slave side of the split keyboard. The purpose of this feature is to support state (on/off state only) syncing. + +```c +#define SPLIT_POINTING_ENABLE +``` + +This enables transmitting the pointing device status to the master side of the split keyboard. The purpose of this feature is to enable use pointing devices on the slave side. + +::: warning +There is additional required configuration for `SPLIT_POINTING_ENABLE` outlined in the [pointing device documentation](pointing_device#split-keyboard-configuration). +::: + +```c +#define SPLIT_HAPTIC_ENABLE +``` + +This enables the triggering of haptic feedback on the slave side of the split keyboard. This will send information to the slave side such as the mode, dwell, and whether buzz is enabled. + +```c +#define SPLIT_ACTIVITY_ENABLE +``` + +This synchronizes the activity timestamps between sides of the split keyboard, allowing for activity timeouts to occur. + +### Custom data sync between sides {#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"); + } + } + } +} +``` + +::: warning +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 + +There are some settings that you may need to configure, based on how the hardware is set up. + +```c +#define MATRIX_ROW_PINS_RIGHT { } +#define MATRIX_COL_PINS_RIGHT { } +``` + +This allows you to specify a different set of pins for the matrix on the right side. This is useful if you have a board with differently-shaped halves that requires a different configuration (such as Keebio's Quefrency). The number of pins in the right and left matrices must be the same, if you have a board with a different number of rows or columns on one side, pad out the extra spaces with `NO_PIN` and make sure you add the unused rows or columns to your matrix. + +```c +#define DIRECT_PINS_RIGHT { { F1, F0, B0, C7 }, { F4, F5, F6, F7 } } +``` + +This allows you to specify a different set of direct pins for the right side. + +```c +#define ENCODERS_PAD_A_RIGHT { encoder1a, encoder2a } +#define ENCODERS_PAD_B_RIGHT { encoder1b, encoder2b } +``` + +This allows you to specify a different set of encoder pins for the right side. + +```c +#define RGBLIGHT_SPLIT +``` + +This option enables synchronization of the RGB Light modes between the controllers of the split keyboard. This is for keyboards that have RGB LEDs that are directly wired to the controller (that is, they are not using the "extra data" option on the TRRS cable). + +```c +#define RGBLED_SPLIT { 6, 6 } +``` + +This sets how many LEDs are directly connected to each controller. The first number is the left side, and the second number is the right side. + +::: tip +This setting implies that `RGBLIGHT_SPLIT` is enabled, and will forcibly enable it, if it's not. +::: + + +```c +#define SPLIT_USB_DETECT +``` + +Enabling this option changes the startup behavior to listen for an active USB communication to delegate which part is master and which is slave. With this option enabled and theres's USB communication, then that half assumes it is the master, otherwise it assumes it is the slave. + +Without this option, the master is the half that can detect voltage on the physical USB connection (VBUS detection). + +Enabled by default on ChibiOS/ARM. + +::: tip +This setting will stop the ability to demo using battery packs. +::: + +```c +#define SPLIT_USB_TIMEOUT 2000 +``` +This sets the maximum timeout when detecting master/slave when using `SPLIT_USB_DETECT`. + +```c +#define SPLIT_USB_TIMEOUT_POLL 10 +``` +This sets the poll frequency when detecting master/slave when using `SPLIT_USB_DETECT` + +```c +#define SPLIT_WATCHDOG_ENABLE +``` + +This will enable a software watchdog on any side delegated as slave and will reboot the keyboard if no successful communication occurs within `SPLIT_WATCHDOG_TIMEOUT`. This can be particularly helpful when `SPLIT_USB_DETECT` delegates both sides as slave in some circumstances. + +```c +#define SPLIT_WATCHDOG_TIMEOUT 3000 +``` +This set the maximum slave timeout when waiting for communication from master when using `SPLIT_WATCHDOG_ENABLE` + +## Hardware Considerations and Mods + +Master/slave delegation is made either by detecting voltage on VBUS connection or waiting for USB communication (`SPLIT_USB_DETECT`). Pro Micro boards can use VBUS detection out of the box and be used with or without `SPLIT_USB_DETECT`. + +Many ARM boards, but not all, do not support VBUS detection. Because it is common that ARM boards lack VBUS detection, `SPLIT_USB_DETECT` is automatically defined on ARM targets (technically when ChibiOS is targetted). + +### Teensy boards + +Teensy boards lack VBUS detection out of the box and must have `SPLIT_USB_DETECT` defined. With the Teensy 2.0 and Teensy++ 2.0, there is a simple hardware mod that you can perform to add VBUS detection, so you don't need the `SPLIT_USB_DETECT` option. + +You'll only need a few things: + +* A knife (x-acto knife, ideally) +* A solder station or hot air station +* An appropriate Schottky diode, such as the [PMEG2005EH](https://www.digikey.com/en/products/detail/nexperia-usa-inc/PMEG2005EH,115/1589924) + +You'll need to cut the small trace between the 5V and center pads on the back of the Teensy. + +Once you have done that, you will want to solder the diode from the 5V pad to the center pad. + +You may need to use the 5V pad from the regulator block above as the pads were too small and placed too closely together to place the Schottky diode properly. + +![Teensy++ 2.0](https://i.imgur.com/BPEC5n5.png) + +## Additional Resources + +Nicinabox has a [very nice and detailed guide](https://github.com/nicinabox/lets-split-guide) for the Let's Split keyboard, that covers most everything you need to know, including troubleshooting information. + +However, the RGB Light section is out of date, as it was written long before the RGB Split code was added to QMK Firmware. Instead, wire each strip up directly to the controller. + + diff --git a/docs/features/st7565.md b/docs/features/st7565.md new file mode 100644 index 0000000000..de3e44d8e9 --- /dev/null +++ b/docs/features/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.
`(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.
`(sizeof(ST7565_BLOCK_TYPE) * 8)`.| +|`ST7565_BLOCK_SIZE` |`32` |The size of each block for dirty rendering
`(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/features/stenography.md b/docs/features/stenography.md new file mode 100644 index 0000000000..c6c2155a9a --- /dev/null +++ b/docs/features/stenography.md @@ -0,0 +1,221 @@ +# Stenography in QMK {#stenography-in-qmk} + +[Stenography](https://en.wikipedia.org/wiki/Stenotype) is a method of writing most often used by court reports, closed-captioning, and real-time transcription for the deaf. In stenography words are chorded syllable by syllable with a mixture of spelling, phonetic, and shortcut (briefs) strokes. Professional stenographers can reach 200-300 WPM without any of the strain usually found in standard typing and with far fewer errors (>99.9% accuracy). + +The [Open Steno Project](https://www.openstenoproject.org/) has built an open-source program called Plover that provides real-time translation of steno strokes into words and commands. It has an established dictionary and supports + +## Plover with QWERTY Keyboard {#plover-with-qwerty-keyboard} + +Plover can work with any standard QWERTY keyboard, although it is more efficient if the keyboard supports NKRO (n-key rollover) to allow Plover to see all the pressed keys at once. An example keymap for Plover can be found in `planck/keymaps/default`. Switching to the `PLOVER` layer adjusts the position of the keyboard to support the number bar. + +To enable NKRO, add `NKRO_ENABLE = yes` in your `rules.mk` and make sure to press `NK_ON` to turn it on because `NKRO_ENABLE = yes` merely adds the possibility of switching to NKRO mode but it doesn't automatically switch to it. If you want to automatically switch, add `#define FORCE_NKRO` in your `config.h`. + +You may also need to adjust your layout, either in QMK or in Plover, if you have anything other than a standard layout. You may also want to purchase some steno-friendly keycaps to make it easier to hit multiple keys. + +## Plover with Steno Protocol {#plover-with-steno-protocol} + +Plover also understands the language of several steno machines. QMK can speak a couple of these languages: TX Bolt and GeminiPR. An example layout can be found in `planck/keymaps/steno`. + +When QMK speaks to Plover over a steno protocol, Plover will not use the keyboard as input. This means that you can switch back and forth between a standard keyboard and your steno keyboard, or even switch layers from Plover to standard and back without needing to activate/deactivate Plover. + +In this mode, Plover expects to speak with a steno machine over a serial port so QMK will present itself to the operating system as a virtual serial port in addition to a keyboard. + +::: info +Note: Due to hardware limitations, you might not be able to run both a virtual serial port and mouse emulation at the same time. +::: + +::: warning +Serial stenography protocols are not supported on [V-USB keyboards](../compatible_microcontrollers#atmel-avr). +::: + +To enable stenography protocols, add the following lines to your `rules.mk`: +```make +STENO_ENABLE = yes +``` + +### TX Bolt {#tx-bolt} + +TX Bolt communicates the status of 24 keys over a simple protocol in variable-sized (1–4 bytes) packets. + +To select TX Bolt, add the following lines to your `rules.mk`: +```make +STENO_ENABLE = yes +STENO_PROTOCOL = txbolt +``` + +Each byte of the packet represents a different group of steno keys. Determining the group of a certain byte of the packet is done by checking the first two bits, the remaining bits are set if the corresponding steno key was pressed for the stroke. The last set of keys (as indicated by leading `11`) needs to keep track of less keys than there are bits so one of the bits is constantly 0. + +The start of a new packet can be detected by comparing the group “ID” (the two MSBs) of the current byte to that of the previously received byte. If the group “ID” of the current byte is smaller or equal to that of the previous byte, it means that the current byte is the beginning of a new packet. + +The format of TX Bolt packets is shown below. +``` +00HWPKTS 01UE*OAR 10GLBPRF 110#ZDST +``` + +Examples of steno strokes and the associated packet: +- `EUBG` = `01110000 10101000` +- `WAZ` = `00010000 01000010 11001000` +- `PHAPBGS` = `00101000 01000010 10101100 11000010` + +### GeminiPR {#geminipr} + +GeminiPR encodes 42 keys into a 6-byte packet. While TX Bolt contains everything that is necessary for standard stenography, GeminiPR opens up many more options, including differentiating between top and bottom `S-`, and supporting non-English theories. + +To select GeminiPR, add the following lines to your `rules.mk`: +```make +STENO_ENABLE = yes +STENO_PROTOCOL = geminipr +``` + +All packets in the GeminiPR protocol consist of exactly six bytes, used as bit-arrays for different groups of keys. The beginning of a packet is indicated by setting the most significant bit (MSB) to 1 while setting the MSB of the remaining five bytes to 0. + +The format of GeminiPR packets is shown below. +``` +1 Fn #1 #2 #3 #4 #5 #6 +0 S1- S2- T- K- P- W- H- +0 R- A- O- *1 *2 res1 res2 +0 pwr *3 *4 -E -U -F -R +0 -P -B -L -G -T -S -D +0 #7 #8 #9 #A #B #C -Z +``` + +Examples of steno strokes and the associated packet: +- `EUBG` = `10000000 00000000 00000000 00001100 00101000 00000000` +- `WAZ` = `10000000 00000010 00100000 00000000 00000000 00000001` +- `PHAPBGS` = `10000000 00000101 00100000 00000000 01101010 00000000` + +### Switching protocols on the fly {#switching-protocols-on-the-fly} + +If you wish to switch the serial protocol used to transfer the steno chords without having to recompile your keyboard firmware every time, you can press the `QK_STENO_BOLT` and `QK_STENO_GEMINI` keycodes in order to switch protocols on the fly. + +To enable these special keycodes, add the following lines to your `rules.mk`: +```make +STENO_ENABLE = yes +STENO_PROTOCOL = all +``` + +If you want to switch protocols programatically, as part of a custom macro for example, don't use `tap_code(QK_STENO_*)`, as `tap_code` only supports [basic keycodes](../keycodes_basic). Instead, you should use `steno_set_mode(STENO_MODE_*)`, whose valid arguments are `STENO_MODE_BOLT` and `STENO_MODE_GEMINI`. + +The default protocol is Gemini PR but the last protocol used is stored in non-volatile memory so QMK will remember your choice between reboots of your keyboard — assuming that your keyboard features (emulated) EEPROM. + +Naturally, this option takes the most amount of firmware space as it needs to compile the code for all the available stenography protocols. In most cases, compiling a single stenography protocol is sufficient. + +The default value for `STENO_PROTOCOL` is `all`. + +## Configuring QMK for Steno {#configuring-qmk-for-steno} + +After enabling stenography and optionally selecting a protocol, you may also need disable mouse keys, extra keys, or another USB endpoint to prevent conflicts. The builtin USB stack for some processors only supports a certain number of USB endpoints and the virtual serial port needed for steno fills 3 of them. + +::: warning +If you had *explicitly* set `VIRSTER_ENABLE = no`, none of the serial stenography protocols (GeminiPR, TX Bolt) will work properly. You are expected to either set it to `yes`, remove the line from your `rules.mk` or send the steno chords yourself in an alternative way using the [provided interceptable hooks](#interfacing-with-the-code). +::: + +In your keymap, create a new layer for Plover, that you can fill in with the [steno keycodes](#keycode-reference). Remember to create a key to switch to the layer as well as a key for exiting the layer. + +Once you have your keyboard flashed, launch Plover. Click the 'Configure...' button. In the 'Machine' tab, select the Stenotype Machine that corresponds to your desired protocol. Click the 'Configure...' button on this tab and enter the serial port or click 'Scan'. Baud rate is fine at 9600 (although you should be able to set as high as 115200 with no issues). Use the default settings for everything else (Data Bits: 8, Stop Bits: 1, Parity: N, no flow control). + +To test your keymap, you can chord keys on your keyboard and either look at the output of the 'paper tape' (Tools > Paper Tape) or that of the 'layout display' (Tools > Layout Display). If your strokes correctly show up, you are now ready to steno! + +## Learning Stenography {#learning-stenography} + +* [Learn Plover!](https://sites.google.com/site/learnplover/) +* [Steno Jig](https://joshuagrams.github.io/steno-jig/) +* More resources at the Plover [Learning Stenography](https://github.com/openstenoproject/plover/wiki/Learning-Stenography) wiki + +## Interfacing with the code {#interfacing-with-the-code} + +The steno code has three interceptable hooks. If you define these functions, they will be called at certain points in processing; if they return true, processing continues, otherwise it's assumed you handled things. + +```c +bool send_steno_chord_user(steno_mode_t mode, uint8_t chord[MAX_STROKE_SIZE]); +``` + +This function is called when a chord is about to be sent. Mode will be one of `STENO_MODE_BOLT` or `STENO_MODE_GEMINI`. This represents the actual chord that would be sent via whichever protocol. You can modify the chord provided to alter what gets sent. Remember to return true if you want the regular sending process to happen. + +```c +bool process_steno_user(uint16_t keycode, keyrecord_t *record) { return true; } +``` + +This function is called when a keypress has come in, before it is processed. The keycode should be one of `QK_STENO_BOLT`, `QK_STENO_GEMINI`, or one of the `STN_*` key values. + +```c +bool post_process_steno_user(uint16_t keycode, keyrecord_t *record, steno_mode_t mode, uint8_t chord[MAX_STROKE_SIZE], int8_t n_pressed_keys); +``` + +This function is called after a key has been processed, but before any decision about whether or not to send a chord. This is where to put hooks for things like, say, live displays of steno chords or keys. + +If `record->event.pressed` is false, and `n_pressed_keys` is 0 or 1, the chord will be sent shortly, but has not yet been sent. This relieves you of the need of keeping track of where a packet ends and another begins. + +The `chord` argument contains the packet of the current chord as specified by the protocol in use. This is *NOT* simply a list of chorded steno keys of the form `[STN_E, STN_U, STN_BR, STN_GR]`. Refer to the appropriate protocol section of this document to learn more about the format of the packets in your steno protocol/mode of choice. + +The `n_pressed_keys` argument is the number of physical keys actually being held down. +This is not always equal to the number of bits set to 1 (aka the [Hamming weight](https://en.wikipedia.org/wiki/Hamming_weight)) in `chord` because it is possible to simultaneously press down four keys, then release three of those four keys and then press yet another key while the fourth finger is still holding down its key. +At the end of this scenario given as an example, `chord` would have five bits set to 1 but +`n_pressed_keys` would be set to 2 because there are only two keys currently being pressed down. + +## Keycode Reference {#keycode-reference} + +::: info +Note: TX Bolt does not support the full set of keys. The TX Bolt implementation in QMK will map the GeminiPR keys to the nearest TX Bolt key so that one key map will work for both. +::: + +|GeminiPR|TX Bolt|Steno Key| +|--------|-------|-----------| +|`STN_N1`|`STN_NUM`|Number bar #1| +|`STN_N2`|`STN_NUM`|Number bar #2| +|`STN_N3`|`STN_NUM`|Number bar #3| +|`STN_N4`|`STN_NUM`|Number bar #4| +|`STN_N5`|`STN_NUM`|Number bar #5| +|`STN_N6`|`STN_NUM`|Number bar #6| +|`STN_N7`|`STN_NUM`|Number bar #7| +|`STN_N8`|`STN_NUM`|Number bar #8| +|`STN_N9`|`STN_NUM`|Number bar #9| +|`STN_NA`|`STN_NUM`|Number bar #A| +|`STN_NB`|`STN_NUM`|Number bar #B| +|`STN_NC`|`STN_NUM`|Number bar #C| +|`STN_S1`|`STN_SL`| `S-` upper| +|`STN_S2`|`STN_SL`| `S-` lower| +|`STN_TL`|`STN_TL`| `T-`| +|`STN_KL`|`STN_KL`| `K-`| +|`STN_PL`|`STN_PL`| `P-`| +|`STN_WL`|`STN_WL`| `W-`| +|`STN_HL`|`STN_HL`| `H-`| +|`STN_RL`|`STN_RL`| `R-`| +|`STN_A`|`STN_A`| `A` vowel| +|`STN_O`|`STN_O`| `O` vowel| +|`STN_ST1`|`STN_STR`| `*` upper-left | +|`STN_ST2`|`STN_STR`| `*` lower-left| +|`STN_ST3`|`STN_STR`| `*` upper-right| +|`STN_ST4`|`STN_STR`| `*` lower-right| +|`STN_E`|`STN_E`| `E` vowel| +|`STN_U`|`STN_U`| `U` vowel| +|`STN_FR`|`STN_FR`| `-F`| +|`STN_RR`|`STN_RR`| `-R`| +|`STN_PR`|`STN_PR`| `-P`| +|`STN_BR`|`STN_BR`| `-B`| +|`STN_LR`|`STN_LR`| `-L`| +|`STN_GR`|`STN_GR`| `-G`| +|`STN_TR`|`STN_TR`| `-T`| +|`STN_SR`|`STN_SR`| `-S`| +|`STN_DR`|`STN_DR`| `-D`| +|`STN_ZR`|`STN_ZR`| `-Z`| +|`STN_FN`|| (Function)| +|`STN_RES1`||(Reset 1)| +|`STN_RES2`||(Reset 2)| +|`STN_PWR`||(Power)| + +If you do not want to hit two keys with one finger combined keycodes can be used. These cause both keys to be reported as pressed or released. To use these keycodes define `STENO_COMBINEDMAP` in your `config.h` file. + +|Combined key | Key1 | Key 2 | +|---------------|--------|----------| +|STN_S3 | STN_S1 | STN_S2 | +|STN_TKL | STN_TL | STN_KL | +|STN_PWL | STN_PL | STN_WL | +|STN_HRL | STN_HL | STN_RL | +|STN_FRR | STN_FR | STN_RR | +|STN_PBR | STN_PR | STN_BR | +|STN_LGR | STN_LR | STN_GR | +|STN_TSR | STN_TR | STN_SR | +|STN_DZR | STN_DR | STN_ZR | +|STN_AO | STN_A | STN_O | +|STN_EU | STN_E | STN_U | diff --git a/docs/features/swap_hands.md b/docs/features/swap_hands.md new file mode 100644 index 0000000000..3560fe22f0 --- /dev/null +++ b/docs/features/swap_hands.md @@ -0,0 +1,57 @@ +# Swap-Hands Action + +The swap-hands action allows support for one-handed typing without requiring a separate layer. Set `SWAP_HANDS_ENABLE` in the Makefile and define a `hand_swap_config` entry in your keymap. Now whenever the `ACTION_SWAP_HANDS` command key is pressed the keyboard is mirrored. For instance, to type "Hello, World" on QWERTY you would type `^Ge^s^s^w^c W^wr^sd` + +## Configuration + +The configuration table is a simple 2-dimensional array to map from column/row to new column/row. Example `hand_swap_config` for Planck: + +```c +const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { + {{11, 0}, {10, 0}, {9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}}, + {{11, 1}, {10, 1}, {9, 1}, {8, 1}, {7, 1}, {6, 1}, {5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}}, + {{11, 2}, {10, 2}, {9, 2}, {8, 2}, {7, 2}, {6, 2}, {5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}}, + {{11, 3}, {10, 3}, {9, 3}, {8, 3}, {7, 3}, {6, 3}, {5, 3}, {4, 3}, {3, 3}, {2, 3}, {1, 3}, {0, 3}}, +}; +``` + +Note that the array indices are reversed same as the matrix and the values are of type `keypos_t` which is `{col, row}` and all values are zero-based. In the example above, `hand_swap_config[2][4]` (third row, fifth column) would return `{7, 2}` (third row, eighth column). Yes, this is confusing. + +## Swap Keycodes + +|Key |Aliases |Description | +|-----------------------------|---------|----------------------------------------------------| +|`SH_T(kc)` | |Momentary swap when held, `kc` when tapped | +|`QK_SWAP_HANDS_ON` |`SH_ON` |Turn on hand swap | +|`QK_SWAP_HANDS_OFF` |`SH_OFF` |Turn off hand swap | +|`QK_SWAP_HANDS_MOMENTARY_ON` |`SH_MON` |Turn on hand swap while held | +|`QK_SWAP_HANDS_MOMENTARY_OFF`|`SH_MOFF`|Turn off hand swap while held | +|`QK_SWAP_HANDS_TOGGLE` |`SH_TOGG`|Toggle hand swap | +|`QK_SWAP_HANDS_TAP_TOGGLE` |`SH_TT` |Momentary swap when held, toggle when tapped | +|`QK_SWAP_HANDS_ONE_SHOT` |`SH_OS` |Turn on hand swap while held or until next key press| + +`SH_TT` swap-hands tap-toggle key is similar to [layer tap-toggle](../feature_layers#switching-and-toggling-layers). Tapping repeatedly (5 taps by default) will toggle swap-hands on or off, like `SH_TOGG`. Tap-toggle count can be changed by defining a value for `TAPPING_TOGGLE`. + +## Encoder Mapping + +When using an encoder mapping, it's also able to handle swapping encoders between sides, too. + +Encoder indexes are defined as left-to-right, and the extent of the array needs to match the number of encoders on the keyboard. + +As an example, if a split keyboard has a single encoder per side, you can swap the order by using the following code in your keymap: +```c +#if defined(SWAP_HANDS_ENABLE) && defined(ENCODER_MAP_ENABLE) +const uint8_t PROGMEM encoder_hand_swap_config[NUM_ENCODERS] = { 1, 0 }; +#endif +``` + +### Functions {#functions} + +User callback functions to manipulate Swap-Hands: + +| Function | Description | +|-----------------------|---------------------------------------------| +| `swap_hands_on()` | Turns Swap-Hands on. | +| `swap_hands_off()` | Turns Swap-Hands off. | +| `swap_hands_toggle()` | Toggles Swap-Hands. | +| `is_swap_hands_on()` | Returns true if Swap-Hands is currently on. | diff --git a/docs/features/tap_dance.md b/docs/features/tap_dance.md new file mode 100644 index 0000000000..3c4040db22 --- /dev/null +++ b/docs/features/tap_dance.md @@ -0,0 +1,567 @@ +# Tap Dance: A Single Key Can Do 3, 5, or 100 Different Things + +## Introduction {#introduction} + +Hit the semicolon key once, send a semicolon. Hit it twice, rapidly -- send a colon. Hit it three times, and your keyboard's LEDs do a wild dance. That's just one example of what Tap Dance can do. It's one of the nicest community-contributed features in the firmware, conceived and created by [algernon](https://github.com/algernon) in [#451](https://github.com/qmk/qmk_firmware/pull/451). Here's how algernon describes the feature: + +With this feature one can specify keys that behave differently, based on the amount of times they have been tapped, and when interrupted, they get handled before the interrupter. + +## How to Use Tap Dance {#how-to-use} + +First, you will need `TAP_DANCE_ENABLE = yes` in your `rules.mk`, because the feature is disabled by default. This adds a little less than 1k to the firmware size. + +Optionally, you might want to set a custom `TAPPING_TERM` time by adding something like this in your `config.h` file: + +```c +#define TAPPING_TERM 175 +#define TAPPING_TERM_PER_KEY +``` + +The `TAPPING_TERM` time is the maximum time allowed between taps of your Tap Dance key, and is measured in milliseconds. For example, if you used the above `#define` statement and set up a Tap Dance key that sends `Space` on single-tap and `Enter` on double-tap, then this key will send `ENT` only if you tap this key twice in less than 175ms. If you tap the key, wait more than 175ms, and tap the key again you'll end up sending `SPC SPC` instead. The `TAPPING_TERM_PER_KEY` definition is only needed if you control the tapping term through a [custom `get_tapping_term` function](../tap_hold#tapping_term), which may be needed because `TAPPING_TERM` affects not just tap-dance keys. + +Next, you will want to define some tap-dance keys, which is easiest to do with the `TD()` macro. That macro takes a number which will later be used as an index into the `tap_dance_actions` array and turns it into a tap-dance keycode. + +After this, you'll want to use the `tap_dance_actions` array to specify what actions shall be taken when a tap-dance key is in action. Currently, there are five possible options: + +* `ACTION_TAP_DANCE_DOUBLE(kc1, kc2)`: Sends the `kc1` keycode when tapped once, `kc2` otherwise. When the key is held, the appropriate keycode is registered: `kc1` when pressed and held, `kc2` when tapped once, then pressed and held. +* `ACTION_TAP_DANCE_LAYER_MOVE(kc, layer)`: Sends the `kc` keycode when tapped once, or moves to `layer`. (this functions like the `TO` layer keycode). +* `ACTION_TAP_DANCE_LAYER_TOGGLE(kc, layer)`: Sends the `kc` keycode when tapped once, or toggles the state of `layer`. (this functions like the `TG` layer keycode). +* `ACTION_TAP_DANCE_FN(fn)`: Calls the specified function - defined in the user keymap - with the final tap count of the tap dance action. +* `ACTION_TAP_DANCE_FN_ADVANCED(on_each_tap_fn, on_dance_finished_fn, on_dance_reset_fn)`: Calls the first specified function - defined in the user keymap - on every tap, the second function when the dance action finishes (like the previous option), and the last function when the tap dance action resets. +* `ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(on_each_tap_fn, on_each_release_fn, on_dance_finished_fn, on_dance_reset_fn)`: This macro is identical to `ACTION_TAP_DANCE_FN_ADVANCED` with the addition of `on_each_release_fn` which is invoked every time the key for the tap dance is released. It is worth noting that `on_each_release_fn` will still be called even when the key is released after the dance finishes (e.g. if the key is released after being pressed and held for longer than the `TAPPING_TERM`). + +The first option is enough for a lot of cases, that just want dual roles. For example, `ACTION_TAP_DANCE_DOUBLE(KC_SPC, KC_ENT)` will result in `Space` being sent on single-tap, `Enter` otherwise. + +::: warning +Keep in mind that only [basic keycodes](../keycodes_basic) are supported here. Custom keycodes are not supported. +::: + +Similar to the first option, the second and third option are good for simple layer-switching cases. + +For more complicated cases, like blink the LEDs, fiddle with the backlighting, and so on, use the fourth or fifth option. Examples of each are listed below. + +## Implementation Details {#implementation} + +Well, that's the bulk of it! You should now be able to work through the examples below, and to develop your own Tap Dance functionality. But if you want a deeper understanding of what's going on behind the scenes, then read on for the explanation of how it all works! + +Let's go over the three functions mentioned in `ACTION_TAP_DANCE_FN_ADVANCED` in a little more detail. They all receive the same two arguments: a pointer to a structure that holds all dance related state information, and a pointer to a use case specific state variable. The three functions differ in when they are called. The first, `on_each_tap_fn()`, is called every time the tap dance key is *pressed*. Before it is called, the counter is incremented and the timer is reset. The second function, `on_dance_finished_fn()`, is called when the tap dance is interrupted or ends because `TAPPING_TERM` milliseconds have passed since the last tap. When the `finished` field of the dance state structure is set to `true`, the `on_dance_finished_fn()` is skipped. After `on_dance_finished_fn()` was called or would have been called, but no sooner than when the tap dance key is *released*, `on_dance_reset_fn()` is called. It is possible to end a tap dance immediately, skipping `on_dance_finished_fn()`, but not `on_dance_reset_fn`, by calling `reset_tap_dance(state)`. + +To accomplish this logic, the tap dance mechanics use three entry points. The main entry point is `process_tap_dance()`, called from `process_record_quantum()` *after* `process_record_kb()` and `process_record_user()`. This function is responsible for calling `on_each_tap_fn()` and `on_dance_reset_fn()`. In order to handle interruptions of a tap dance, another entry point, `preprocess_tap_dance()` is run right at the beginning of `process_record_quantum()`. This function checks whether the key pressed is a tap-dance key. If it is not, and a tap-dance was in action, we handle that first, and enqueue the newly pressed key. If it is a tap-dance key, then we check if it is the same as the already active one (if there's one active, that is). If it is not, we fire off the old one first, then register the new one. Finally, `tap_dance_task()` periodically checks whether `TAPPING_TERM` has passed since the last key press and finishes a tap dance if that is the case. + +This means that you have `TAPPING_TERM` time to tap the key again; you do not have to input all the taps within a single `TAPPING_TERM` timeframe. This allows for longer tap counts, with minimal impact on responsiveness. + +## Examples {#examples} + +### Simple Example: Send `ESC` on Single Tap, `CAPS_LOCK` on Double Tap {#simple-example} + +Here's a simple example for a single definition: + +1. In your `rules.mk`, add `TAP_DANCE_ENABLE = yes` +2. In your `keymap.c` file, define the variables and definitions, then add to your keymap: + +```c +// Tap Dance declarations +enum { + TD_ESC_CAPS, +}; + +// Tap Dance definitions +tap_dance_action_t tap_dance_actions[] = { + // Tap once for Escape, twice for Caps Lock + [TD_ESC_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_ESC, KC_CAPS), +}; + +// Add tap dance item to your keymap in place of a keycode +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + // ... + TD(TD_ESC_CAPS) + // ... +}; +``` + +### Complex Examples {#complex-examples} + +This section details several complex tap dance examples. +All the enums used in the examples are declared like this: + +```c +// Enums defined for all examples: +enum { + TD_ESC_CAPS, + CT_EGG, + CT_FLSH, + CT_CLN, + X_CTL, +}; +``` + +#### Example 1: Send "Safety Dance!" After 100 Taps {#example-1} + +```c +void dance_egg(tap_dance_state_t *state, void *user_data) { + if (state->count >= 100) { + SEND_STRING("Safety dance!"); + reset_tap_dance(state); + } +} + +tap_dance_action_t tap_dance_actions[] = { + [CT_EGG] = ACTION_TAP_DANCE_FN(dance_egg), +}; +``` + +#### Example 2: Turn LED Lights On Then Off, One at a Time {#example-2} + +```c +// On each tap, light up one LED, from right to left +// On the fourth tap, turn them off from right to left +void dance_flsh_each(tap_dance_state_t *state, void *user_data) { + switch (state->count) { + case 1: + ergodox_right_led_3_on(); + break; + case 2: + ergodox_right_led_2_on(); + break; + case 3: + ergodox_right_led_1_on(); + break; + case 4: + ergodox_right_led_3_off(); + wait_ms(50); + ergodox_right_led_2_off(); + wait_ms(50); + ergodox_right_led_1_off(); + } +} + +// On the fourth tap, set the keyboard on flash state +void dance_flsh_finished(tap_dance_state_t *state, void *user_data) { + if (state->count >= 4) { + reset_keyboard(); + } +} + +// If the flash state didn't happen, then turn off LEDs, left to right +void dance_flsh_reset(tap_dance_state_t *state, void *user_data) { + ergodox_right_led_1_off(); + wait_ms(50); + ergodox_right_led_2_off(); + wait_ms(50); + ergodox_right_led_3_off(); +} + +// All tap dances now put together. Example 2 is "CT_FLSH" +tap_dance_action_t tap_dance_actions[] = { + [TD_ESC_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_ESC, KC_CAPS), + [CT_EGG] = ACTION_TAP_DANCE_FN(dance_egg), + [CT_FLSH] = ACTION_TAP_DANCE_FN_ADVANCED(dance_flsh_each, dance_flsh_finished, dance_flsh_reset) +}; +``` + +#### Example 3: Send `:` on Tap, `;` on Hold {#example-3} + +With a little effort, powerful tap-hold configurations can be implemented as tap dances. To emit taps as early as possible, we need to act on releases of the tap dance key. There is no callback for this in the tap dance framework, so we use `process_record_user()`. + +```c +typedef struct { + uint16_t tap; + uint16_t hold; + uint16_t held; +} tap_dance_tap_hold_t; + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + tap_dance_action_t *action; + + switch (keycode) { + case TD(CT_CLN): // list all tap dance keycodes with tap-hold configurations + action = &tap_dance_actions[QK_TAP_DANCE_GET_INDEX(keycode)]; + if (!record->event.pressed && action->state.count && !action->state.finished) { + tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)action->user_data; + tap_code16(tap_hold->tap); + } + } + return true; +} + +void tap_dance_tap_hold_finished(tap_dance_state_t *state, void *user_data) { + tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data; + + if (state->pressed) { + if (state->count == 1 +#ifndef PERMISSIVE_HOLD + && !state->interrupted +#endif + ) { + register_code16(tap_hold->hold); + tap_hold->held = tap_hold->hold; + } else { + register_code16(tap_hold->tap); + tap_hold->held = tap_hold->tap; + } + } +} + +void tap_dance_tap_hold_reset(tap_dance_state_t *state, void *user_data) { + tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data; + + if (tap_hold->held) { + unregister_code16(tap_hold->held); + tap_hold->held = 0; + } +} + +#define ACTION_TAP_DANCE_TAP_HOLD(tap, hold) \ + { .fn = {NULL, tap_dance_tap_hold_finished, tap_dance_tap_hold_reset}, .user_data = (void *)&((tap_dance_tap_hold_t){tap, hold, 0}), } + +tap_dance_action_t tap_dance_actions[] = { + [CT_CLN] = ACTION_TAP_DANCE_TAP_HOLD(KC_COLN, KC_SCLN), +}; +``` + +#### Example 4: 'Quad Function Tap-Dance' {#example-4} + +By [DanielGGordon](https://github.com/danielggordon) + +Allow one key to have 4 (or more) functions, depending on number of presses, and if the key is held or tapped. +Below is a specific example: +* Tap = Send `x` +* Hold = Send `Control` +* Double Tap = Send `Escape` +* Double Tap and Hold = Send `Alt` + +You will need a few things that can be used for 'Quad Function Tap-Dance'. + +You'll need to add these to the top of your `keymap.c` file, before your keymap. + +```c +typedef enum { + TD_NONE, + TD_UNKNOWN, + TD_SINGLE_TAP, + TD_SINGLE_HOLD, + TD_DOUBLE_TAP, + TD_DOUBLE_HOLD, + TD_DOUBLE_SINGLE_TAP, // Send two single taps + TD_TRIPLE_TAP, + TD_TRIPLE_HOLD +} td_state_t; + +typedef struct { + bool is_press_action; + td_state_t state; +} td_tap_t; + +// Tap dance enums +enum { + X_CTL, + SOME_OTHER_DANCE +}; + +td_state_t cur_dance(tap_dance_state_t *state); + +// For the x tap dance. Put it here so it can be used in any keymap +void x_finished(tap_dance_state_t *state, void *user_data); +void x_reset(tap_dance_state_t *state, void *user_data); +``` + +Now, at the bottom of your `keymap.c` file, you'll need to add the following: + +```c +/* Return an integer that corresponds to what kind of tap dance should be executed. + * + * How to figure out tap dance state: interrupted and pressed. + * + * Interrupted: If the state of a dance is "interrupted", that means that another key has been hit + * under the tapping term. This is typically indicitive that you are trying to "tap" the key. + * + * Pressed: Whether or not the key is still being pressed. If this value is true, that means the tapping term + * has ended, but the key is still being pressed down. This generally means the key is being "held". + * + * One thing that is currenlty not possible with qmk software in regards to tap dance is to mimic the "permissive hold" + * feature. In general, advanced tap dances do not work well if they are used with commonly typed letters. + * For example "A". Tap dances are best used on non-letter keys that are not hit while typing letters. + * + * Good places to put an advanced tap dance: + * z,q,x,j,k,v,b, any function key, home/end, comma, semi-colon + * + * Criteria for "good placement" of a tap dance key: + * Not a key that is hit frequently in a sentence + * Not a key that is used frequently to double tap, for example 'tab' is often double tapped in a terminal, or + * in a web form. So 'tab' would be a poor choice for a tap dance. + * Letters used in common words as a double. For example 'p' in 'pepper'. If a tap dance function existed on the + * letter 'p', the word 'pepper' would be quite frustating to type. + * + * For the third point, there does exist the 'TD_DOUBLE_SINGLE_TAP', however this is not fully tested + * + */ +td_state_t cur_dance(tap_dance_state_t *state) { + if (state->count == 1) { + if (state->interrupted || !state->pressed) return TD_SINGLE_TAP; + // Key has not been interrupted, but the key is still held. Means you want to send a 'HOLD'. + else return TD_SINGLE_HOLD; + } else if (state->count == 2) { + // TD_DOUBLE_SINGLE_TAP is to distinguish between typing "pepper", and actually wanting a double tap + // action when hitting 'pp'. Suggested use case for this return value is when you want to send two + // keystrokes of the key, and not the 'double tap' action/macro. + if (state->interrupted) return TD_DOUBLE_SINGLE_TAP; + else if (state->pressed) return TD_DOUBLE_HOLD; + else return TD_DOUBLE_TAP; + } + + // Assumes no one is trying to type the same letter three times (at least not quickly). + // If your tap dance key is 'KC_W', and you want to type "www." quickly - then you will need to add + // an exception here to return a 'TD_TRIPLE_SINGLE_TAP', and define that enum just like 'TD_DOUBLE_SINGLE_TAP' + if (state->count == 3) { + if (state->interrupted || !state->pressed) return TD_TRIPLE_TAP; + else return TD_TRIPLE_HOLD; + } else return TD_UNKNOWN; +} + +// Create an instance of 'td_tap_t' for the 'x' tap dance. +static td_tap_t xtap_state = { + .is_press_action = true, + .state = TD_NONE +}; + +void x_finished(tap_dance_state_t *state, void *user_data) { + xtap_state.state = cur_dance(state); + switch (xtap_state.state) { + case TD_SINGLE_TAP: register_code(KC_X); break; + case TD_SINGLE_HOLD: register_code(KC_LCTL); break; + case TD_DOUBLE_TAP: register_code(KC_ESC); break; + case TD_DOUBLE_HOLD: register_code(KC_LALT); break; + // Last case is for fast typing. Assuming your key is `f`: + // For example, when typing the word `buffer`, and you want to make sure that you send `ff` and not `Esc`. + // In order to type `ff` when typing fast, the next character will have to be hit within the `TAPPING_TERM`, which by default is 200ms. + case TD_DOUBLE_SINGLE_TAP: tap_code(KC_X); register_code(KC_X); break; + default: break; + } +} + +void x_reset(tap_dance_state_t *state, void *user_data) { + switch (xtap_state.state) { + case TD_SINGLE_TAP: unregister_code(KC_X); break; + case TD_SINGLE_HOLD: unregister_code(KC_LCTL); break; + case TD_DOUBLE_TAP: unregister_code(KC_ESC); break; + case TD_DOUBLE_HOLD: unregister_code(KC_LALT); break; + case TD_DOUBLE_SINGLE_TAP: unregister_code(KC_X); break; + default: break; + } + xtap_state.state = TD_NONE; +} + +tap_dance_action_t tap_dance_actions[] = { + [X_CTL] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, x_finished, x_reset) +}; +``` + +And then simply use `TD(X_CTL)` anywhere in your keymap. + +::: info +In this configuration "hold" takes place **after** tap dance timeout. To achieve instant hold, remove `state->interrupted` checks in conditions. As a result you may use comfortable longer tapping periods to have more time for taps and not to wait too long for holds (try starting with doubled `TAPPING_TERM`). +::: + +#### Example 5: Using tap dance for advanced mod-tap and layer-tap keys {#example-5} + +Tap dance can be used to emulate `MT()` and `LT()` behavior when the tapped code is not a basic keycode. This is useful to send tapped keycodes that normally require `Shift`, such as parentheses or curly braces—or other modified keycodes, such as `Control + X`. + +Below your layers and custom keycodes, add the following: + +```c +// Tap Dance keycodes +enum td_keycodes { + ALT_LP // Our example key: `LALT` when held, `(` when tapped. Add additional keycodes for each tapdance. +}; + +// Define a type containing as many tapdance states as you need +typedef enum { + TD_NONE, + TD_UNKNOWN, + TD_SINGLE_TAP, + TD_SINGLE_HOLD, + TD_DOUBLE_SINGLE_TAP +} td_state_t; + +// Create a global instance of the tapdance state type +static td_state_t td_state; + +// Declare your tapdance functions: + +// Function to determine the current tapdance state +td_state_t cur_dance(tap_dance_state_t *state); + +// `finished` and `reset` functions for each tapdance keycode +void altlp_finished(tap_dance_state_t *state, void *user_data); +void altlp_reset(tap_dance_state_t *state, void *user_data); +``` + +Below your `LAYOUT`, define each of the tapdance functions: + +```c +// Determine the tapdance state to return +td_state_t cur_dance(tap_dance_state_t *state) { + if (state->count == 1) { + if (state->interrupted || !state->pressed) return TD_SINGLE_TAP; + else return TD_SINGLE_HOLD; + } + + if (state->count == 2) return TD_DOUBLE_SINGLE_TAP; + else return TD_UNKNOWN; // Any number higher than the maximum state value you return above +} + +// Handle the possible states for each tapdance keycode you define: + +void altlp_finished(tap_dance_state_t *state, void *user_data) { + td_state = cur_dance(state); + switch (td_state) { + case TD_SINGLE_TAP: + register_code16(KC_LPRN); + break; + case TD_SINGLE_HOLD: + register_mods(MOD_BIT(KC_LALT)); // For a layer-tap key, use `layer_on(_MY_LAYER)` here + break; + case TD_DOUBLE_SINGLE_TAP: // Allow nesting of 2 parens `((` within tapping term + tap_code16(KC_LPRN); + register_code16(KC_LPRN); + break; + default: + break; + } +} + +void altlp_reset(tap_dance_state_t *state, void *user_data) { + switch (td_state) { + case TD_SINGLE_TAP: + unregister_code16(KC_LPRN); + break; + case TD_SINGLE_HOLD: + unregister_mods(MOD_BIT(KC_LALT)); // For a layer-tap key, use `layer_off(_MY_LAYER)` here + break; + case TD_DOUBLE_SINGLE_TAP: + unregister_code16(KC_LPRN); + break; + default: + break; + } +} + +// Define `ACTION_TAP_DANCE_FN_ADVANCED()` for each tapdance keycode, passing in `finished` and `reset` functions +tap_dance_action_t tap_dance_actions[] = { + [ALT_LP] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, altlp_finished, altlp_reset) +}; +``` + +Wrap each tapdance keycode in `TD()` when including it in your keymap, e.g. `TD(ALT_LP)`. + +#### Example 6: Using tap dance for momentary-layer-switch and layer-toggle keys {#example-6} + +Tap Dance can be used to mimic MO(layer) and TG(layer) functionality. For this example, we will set up a key to function as `KC_QUOT` on single-tap, as `MO(_MY_LAYER)` on single-hold, and `TG(_MY_LAYER)` on double-tap. + +The first step is to include the following code towards the beginning of your `keymap.c`: + +```c +// Define a type for as many tap dance states as you need +typedef enum { + TD_NONE, + TD_UNKNOWN, + TD_SINGLE_TAP, + TD_SINGLE_HOLD, + TD_DOUBLE_TAP +} td_state_t; + +typedef struct { + bool is_press_action; + td_state_t state; +} td_tap_t; + +enum { + QUOT_LAYR, // Our custom tap dance key; add any other tap dance keys to this enum +}; + +// Declare the functions to be used with your tap dance key(s) + +// Function associated with all tap dances +td_state_t cur_dance(tap_dance_state_t *state); + +// Functions associated with individual tap dances +void ql_finished(tap_dance_state_t *state, void *user_data); +void ql_reset(tap_dance_state_t *state, void *user_data); +``` + +Towards the bottom of your `keymap.c`, include the following code: + +```c +// Determine the current tap dance state +td_state_t cur_dance(tap_dance_state_t *state) { + if (state->count == 1) { + if (!state->pressed) return TD_SINGLE_TAP; + else return TD_SINGLE_HOLD; + } else if (state->count == 2) return TD_DOUBLE_TAP; + else return TD_UNKNOWN; +} + +// Initialize tap structure associated with example tap dance key +static td_tap_t ql_tap_state = { + .is_press_action = true, + .state = TD_NONE +}; + +// Functions that control what our tap dance key does +void ql_finished(tap_dance_state_t *state, void *user_data) { + ql_tap_state.state = cur_dance(state); + switch (ql_tap_state.state) { + case TD_SINGLE_TAP: + tap_code(KC_QUOT); + break; + case TD_SINGLE_HOLD: + layer_on(_MY_LAYER); + break; + case TD_DOUBLE_TAP: + // Check to see if the layer is already set + if (layer_state_is(_MY_LAYER)) { + // If already set, then switch it off + layer_off(_MY_LAYER); + } else { + // If not already set, then switch the layer on + layer_on(_MY_LAYER); + } + break; + default: + break; + } +} + +void ql_reset(tap_dance_state_t *state, void *user_data) { + // If the key was held down and now is released then switch off the layer + if (ql_tap_state.state == TD_SINGLE_HOLD) { + layer_off(_MY_LAYER); + } + ql_tap_state.state = TD_NONE; +} + +// Associate our tap dance key with its functionality +tap_dance_action_t tap_dance_actions[] = { + [QUOT_LAYR] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, ql_finished, ql_reset) +}; + +// Set a long-ish tapping term for tap-dance keys +uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case QK_TAP_DANCE ... QK_TAP_DANCE_MAX: + return 275; + default: + return TAPPING_TERM; + } +} +``` + +The above code is similar to that used in previous examples. The one point to note is that we need to be able to check which layers are active at any time so we can toggle them if needed. To do this we use the `layer_state_is(layer)` function which returns `true` if the given `layer` is active. + +The use of `cur_dance()` and `ql_tap_state` mirrors the above examples. + +The `case: TD_SINGLE_TAP` in `ql_finished` is similar to the above examples. The `TD_SINGLE_HOLD` case works in conjunction with `ql_reset()` to switch to `_MY_LAYER` while the tap dance key is held, and to switch away from `_MY_LAYER` when the key is released. This mirrors the use of `MO(_MY_LAYER)`. The `TD_DOUBLE_TAP` case works by checking whether `_MY_LAYER` is the active layer, and toggling it on or off accordingly. This mirrors the use of `TG(_MY_LAYER)`. + +`tap_dance_actions[]` works similar to the above examples. Note that, additionally, I set a longer tapping term for the tap dance keys. This is because I like my `TAPPING_TERM` to be short (\~175ms) for my non-tap-dance keys but find that this is too quick for me to reliably complete tap dance actions - thus the increased time of 275ms here. In order for the per-key tapping terms to take effect, `TAPPING_TERM_PER_KEY` must be defined in your `config.h`. + +Finally, to get this tap dance key working, be sure to include `TD(QUOT_LAYR)` in your `keymaps[]`. diff --git a/docs/features/tri_layer.md b/docs/features/tri_layer.md new file mode 100644 index 0000000000..c32e65caed --- /dev/null +++ b/docs/features/tri_layer.md @@ -0,0 +1,50 @@ +# Tri Layers {#tri-layers} + +This enables support for the OLKB style "Tri Layer" keycodes. These function similar to the `MO` (momentary) function key, but if both the "Lower" and "Upper" keys are pressed, it activates a third "Adjust" layer. To enable this functionality, add this line to your `rules.mk`: + +```make +TRI_LAYER_ENABLE = yes +``` + +Note that the "upper", "lower" and "adjust" names don't have a particular significance, they are just used to identify and clarify the behavior. Layers are processed from highest numeric value to lowest, however the values are not required to be consecutive. + +For a detailed explanation of how the layer stack works, check out [Keymap Overview](../keymap#keymap-and-layers). + +## Keycodes {#keycodes} + +| Keycode | Alias | Description | +|----------------------|-----------|---------------------------------------------------------------------------------------------------------| +| `QK_TRI_LAYER_LOWER` | `TL_LOWR` | Momentarily enables the "lower" layer. Enables the "adjust" layer if the "upper" layer is also enabled" | +| `QK_TRI_LAYER_UPPER` | `TL_UPPR` | Momentarily enables the "upper" layer. Enables the "adjust" layer if the "lower" layer is also enabled" | + +## Configuration + +To change the default values for the layers, you can change these defines, in your `config.h` + +| Config name | Default | Description | +|--------------------------|---------|------------------------------------------| +| `TRI_LAYER_LOWER_LAYER` | `1` | Sets the default for the "lower" layer. | +| `TRI_LAYER_UPPER_LAYER` | `2` | Sets the default for the "upper" layer. | +| `TRI_LAYER_ADJUST_LAYER` | `3` | Sets the default for the "adjust" layer. | + +Eg, if you wanted to set the "Adjust" layer to be layer 5, you'd add this to your `config.h`: + +```c +#define TRI_LAYER_ADJUST_LAYER 5 +``` + +## Functions + +| Function name | Description | +|----------------------------------------------|-------------------------------------------------| +| `set_tri_layer_lower_layer(layer)` | Changes the "lower" layer*. | +| `set_tri_layer_upper_layer(layer)` | Changes the "upper" layer*. | +| `set_tri_layer_adjust_layer(layer)` | Changes the "adjust" layer*. | +| `set_tri_layer_layers(lower, upper, adjust)` | Sets the "lower", "upper" and "adjust" layers*. | +| `get_tri_layer_lower_layer()` | Gets the current "lower" layer. | +| `get_tri_layer_upper_layer()` | Gets the current "upper" layer. | +| `get_tri_layer_adjust_layer()` | Gets the current "adjust" layer. | + +::: warning +Note: these settings are not persistent, and will be reset to the default on power loss or power cycling of the controller. +::: diff --git a/docs/features/unicode.md b/docs/features/unicode.md new file mode 100644 index 0000000000..7ed178c30d --- /dev/null +++ b/docs/features/unicode.md @@ -0,0 +1,454 @@ +# Unicode {#unicode} + +With a little help from your OS, practically any Unicode character can be input using your keyboard. + +## Caveats {#caveats} + +There are some limitations to this feature. Because there is no "standard" method of Unicode input across all operating systems, each of them require their own setup process on both the host *and* in the firmware, which may involve installation of additional software. This also means Unicode input will not "just work" when the keyboard is plugged into another device. + +## Usage {#usage} + +The core Unicode API can be used purely programmatically. However, there are also additional subsystems which build on top of it and come with keycodes to make things easier. See below for more details. + +Add the following to your keymap's `rules.mk`: + +```make +UNICODE_COMMON = yes +``` + +## Basic Configuration {#basic-configuration} + +Add the following to your `config.h`: + +|Define |Default |Description | +|------------------------|------------------|--------------------------------------------------------------------------------| +|`UNICODE_KEY_MAC` |`KC_LEFT_ALT` |The key to hold when beginning a Unicode sequence with the macOS input mode | +|`UNICODE_KEY_LNX` |`LCTL(LSFT(KC_U))`|The key to tap when beginning a Unicode sequence with the Linux input mode | +|`UNICODE_KEY_WINC` |`KC_RIGHT_ALT` |The key to hold when beginning a Unicode sequence with the WinCompose input mode| +|`UNICODE_SELECTED_MODES`|`-1` |A comma separated list of input modes for cycling through | +|`UNICODE_CYCLE_PERSIST` |`true` |Whether to persist the current Unicode input mode to EEPROM | +|`UNICODE_TYPE_DELAY` |`10` |The amount of time to wait, in milliseconds, between Unicode sequence keystrokes| + +### Audio Feedback {#audio-feedback} + +If you have the [Audio](audio) feature enabled on your board, you can configure it to play sounds when the input mode is changed. + +Add the following to your `config.h`: + +|Define |Default|Description | +|-------------------|-------|-----------------------------------------------------------| +|`UNICODE_SONG_MAC` |*n/a* |The song to play when the macOS input mode is selected | +|`UNICODE_SONG_LNX` |*n/a* |The song to play when the Linux input mode is selected | +|`UNICODE_SONG_BSD` |*n/a* |The song to play when the BSD input mode is selected | +|`UNICODE_SONG_WIN` |*n/a* |The song to play when the Windows input mode is selected | +|`UNICODE_SONG_WINC`|*n/a* |The song to play when the WinCompose input mode is selected| + +## Input Subsystems {#input-subsystems} + +Each of these subsystems have their own pros and cons in terms of flexibility and ease of use. Choose the one that best fits your needs. + +::::tabs + +=== Basic + +This is the easiest to use, albeit somewhat limited. It supports code points up to `U+7FFF`, which covers characters for most modern languages (including East Asian), as well as many symbols, but does not include emoji. + +To enable Basic Unicode, add the following to your `rules.mk`: + +```make +UNICODE_ENABLE = yes +``` + +You can then add `UC(c)` keycodes to your keymap, where *c* is the code point of the desired character (in hexadecimal - the `U+` prefix will not work). For example, `UC(0x40B)` will output [Ћ](https://unicode-table.com/en/040B/), and `UC(0x30C4)` will output [ツ](https://unicode-table.com/en/30C4). + +=== Unicode Map + +Unicode Map supports all possible code points (up to `U+10FFFF`). Here, the code points are stored in a separate mapping table (which may contain at most 16,384 entries), instead of directly in the keymap. + +To enable Unicode Map, add the following to your `rules.mk`: + +```make +UNICODEMAP_ENABLE = yes +``` + +Then, you will need to create a mapping table in your `keymap.c`, and (optionally) an enum for naming the array indices, like so: + +```c +enum unicode_names { + BANG, + IRONY, + SNEK +}; + +const uint32_t PROGMEM unicode_map[] = { + [BANG] = 0x203D, // ‽ + [IRONY] = 0x2E2E, // ⸮ + [SNEK] = 0x1F40D, // 🐍 +}; +``` + +Finally, add `UM(i)` keycodes to your keymap, where *i* is an index into the `unicode_map[]` array. If you defined the enum above, you can use those names instead, for example `UM(BANG)` or `UM(SNEK)`. + +#### Lower and Upper Case Pairs {#unicodemap-pairs} + +Some writing systems have lowercase and uppercase variants of each character, such as å and Å. To make inputting these characters easier, you can use the `UP(i, j)` keycode in your keymap, where *i* and *j* are the mapping table indices of the lowercase and uppercase characters, respectively. If you're holding down Shift or have Caps Lock turned on when you press the key, the uppercase character will be inserted; otherwise, the lowercase character will be inserted. + +```c +const uint32_t PROGMEM unicode_map[] = { + [AE_LOWER] = 0x00E6, // æ + [AE_UPPER] = 0x00C6, // Æ +}; +``` + +This is most useful when creating a keymap for an international layout with special characters. Instead of having to put the lower and upper case versions of a character on separate keys, you can have them both on the same key. This helps blend Unicode keys in with regular keycodes. + +Due to keycode size constraints, *i* and *j* can each only refer to one of the first 128 characters in your `unicode_map`. In other words, 0 ≤ *i* ≤ 127 and 0 ≤ *j* ≤ 127. + +=== UCIS + +As with Unicode Map, the UCIS method also supports all possible code points, and requires the use of a mapping table. However, it works much differently - Unicode characters are input by replacing a typed mnemonic. + +To enable UCIS, add the following to your keymap's `rules.mk`: + +```make +UCIS_ENABLE = yes +``` + +Then, create a mapping table in your `keymap.c`: + +```c +const ucis_symbol_t ucis_symbol_table[] = UCIS_TABLE( + UCIS_SYM("poop", 0x1F4A9), // 💩 + UCIS_SYM("rofl", 0x1F923), // 🤣 + UCIS_SYM("ukr", 0x1F1FA, 0x1F1E6), // 🇺🇦 + UCIS_SYM("look", 0x0CA0, 0x005F, 0x0CA0) // ಠ_ಠ +); +``` + +By default, each table entry may be up to three code points long. This can be changed by adding `#define UCIS_MAX_CODE_POINTS n` to your keymap's `config.h`. + +To invoke UCIS input, the `ucis_start()` function must first be called (for example, in a custom "Unicode" keycode). Then, type the mnemonic for the mapping table entry (such as "rofl"), and hit Space or Enter. The "rofl" text will be backspaced and the emoji inserted. + +:::: + +## Input Modes {#input-modes} + +Unicode input works by typing a sequence of characters, similar to a macro. However, since this sequence depends on your OS, you will need to prepare both your host machine and QMK to recognise and send the correct Unicode input sequences respectively. + +To set the list of enabled input modes, add the `UNICODE_SELECTED_MODES` define to your keymap's `config.h`, for example: + +```c +#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX +// or +#define UNICODE_SELECTED_MODES UNICODE_MODE_MACOS, UNICODE_MODE_WINCOMPOSE +``` + +These modes can then be cycled through using the `UC_NEXT` and `UC_PREV` keycodes. You can also switch to any input mode, even if it is not specified in `UNICODE_SELECTED_MODES`, using their respective keycodes. + +If your keyboard has working EEPROM, it will remember the last used input mode and continue using it on the next power up. This can be disabled by defining `UNICODE_CYCLE_PERSIST` to `false`. + +:::::tabs + +==== macOS + +**Mode Name:** `UNICODE_MODE_MACOS` + +macOS has built-in support for Unicode input as its own input source. It supports all possible code points by way of surrogate pairs for code points above `U+FFFF`. + +To enable, go to **System Preferences → Keyboard → Input Sources**, then add Unicode Hex Input to the list (under Other), and activate it from the input dropdown in the menu bar. Note that this may disable some Option-based shortcuts such as Option+Left and Option+Right. + +==== Linux (IBus) + +**Mode Name:** `UNICODE_MODE_LINUX` + +For Linux distros with IBus, Unicode input is enabled by default, supports all possible code points, and works almost anywhere. Without IBus, it works under GTK apps, but rarely anywhere else. + +Users who would like support in non-GTK apps without IBus may need to resort to a more indirect method, such as creating a custom keyboard layout. + +==== Windows (WinCompose) + +**Mode Name:** `UNICODE_MODE_WINCOMPOSE` + +This mode requires a third-party tool called [WinCompose](https://github.com/samhocevar/wincompose). It supports all possible code points, and is the recommended input mode for Windows. + +To enable, install the [latest release from GitHub](https://github.com/samhocevar/wincompose/releases/latest). Once installed, it will automatically run on startup. This works reliably under all versions of Windows supported by WinCompose. + +==== Windows (HexNumpad) + +**Mode Name:** `UNICODE_MODE_WINDOWS` + +::: warning +This input mode is *not* the "Alt code" system. Alt codes are not Unicode; they instead follow [the Windows-1252 character set](https://en.wikipedia.org/wiki/Alt_code). +::: + +This is Windows' built-in hex numpad Unicode input mode. It only supports code points up to `U+FFFF`, and is not recommended due to reliability and compatibility issues. + +To enable, run the following as an administrator, then reboot: + +``` +reg add "HKCU\Control Panel\Input Method" -v EnableHexNumpad -t REG_SZ -d 1 +``` + +==== Emacs + +**Mode Name:** `UNICODE_MODE_EMACS` + +Emacs supports code point input with the `insert-char` command. + +==== BSD + +**Mode Name:** `UNICODE_MODE_BSD` + +Not currently implemented. If you're a BSD user and want to contribute support for this input mode, please [feel free](../contributing)! + +::::: + +## Keycodes {#keycodes} + +|Key |Aliases |Description | +|----------------------------|---------|----------------------------------------------------------------| +|`UC(c)` | |Send Unicode code point `c`, up to `0x7FFF` | +|`UM(i)` | |Send Unicode code point at index `i` in `unicode_map` | +|`UP(i, j)` | |Send Unicode code point at index `i`, or `j` if Shift/Caps is on| +|`QK_UNICODE_MODE_NEXT` |`UC_NEXT`|Cycle through selected input modes | +|`QK_UNICODE_MODE_PREVIOUS` |`UC_PREV`|Cycle through selected input modes in reverse | +|`QK_UNICODE_MODE_MACOS` |`UC_MAC` |Switch to macOS input | +|`QK_UNICODE_MODE_LINUX` |`UC_LINX`|Switch to Linux input | +|`QK_UNICODE_MODE_WINDOWS` |`UC_WIN` |Switch to Windows input | +|`QK_UNICODE_MODE_BSD` |`UC_BSD` |Switch to BSD input (not implemented) | +|`QK_UNICODE_MODE_WINCOMPOSE`|`UC_WINC`|Switch to Windows input using WinCompose | +|`QK_UNICODE_MODE_EMACS` |`UC_EMAC`|Switch to emacs (`C-x-8 RET`) | + +## API {#api} + +### `uint8_t get_unicode_input_mode(void)` {#api-get-unicode-input-mode} + +Get the current Unicode input mode. + +#### Return Value {#api-get-unicode-input-mode-return-value} + +The currently active Unicode input mode. + +--- + +### `void set_unicode_input_mode(uint8_t mode)` {#api-set-unicode-input-mode} + +Set the Unicode input mode. + +#### Arguments {#api-set-unicode-input-mode-arguments} + + - `uint8_t mode` + The input mode to set. + +--- + +### `void unicode_input_mode_step(void)` {#api-unicode-input-mode-step} + +Change to the next Unicode input mode. + +--- + +### `void unicode_input_mode_step_reverse(void)` {#api-unicode-input-mode-step-reverse} + +Change to the previous Unicode input mode. + +--- + +### `void unicode_input_mode_set_user(uint8_t input_mode)` {#api-unicode-input-mode-set-user} + +User-level callback, invoked when the input mode is changed. + +#### Arguments {#api-unicode-input-mode-set-user-arguments} + + - `uint8_t input_mode` + The new input mode. + +--- + +### `void unicode_input_mode_set_kb(uint8_t input_mode)` {#api-unicode-input-mode-set-kb} + +Keyboard-level callback, invoked when the input mode is changed. + +#### Arguments {#api-unicode-input-mode-set-kb-arguments} + + - `uint8_t input_mode` + The new input mode. + +--- + +### `void unicode_input_start(void)` {#api-unicode-input-start} + +Begin the Unicode input sequence. The exact behavior depends on the currently selected input mode: + + - **macOS**: Hold `UNICODE_KEY_MAC` + - **Linux**: Tap `UNICODE_KEY_LNX` + - **WinCompose**: Tap `UNICODE_KEY_WINC`, then U + - **HexNumpad**: Hold Left Alt, then tap Numpad + + - **Emacs**: Tap Ctrl+X, then 8, then Enter + +This function is weakly defined, and can be overridden in user code. + +--- + +### `void unicode_input_finish(void)` {#api-unicode-input-finish} + +Complete the Unicode input sequence. The exact behavior depends on the currently selected input mode: + + - **macOS**: Release `UNICODE_KEY_MAC` + - **Linux**: Tap Space + - **WinCompose**: Tap Enter + - **HexNumpad**: Release Left Alt + - **Emacs**: Tap Enter + +This function is weakly defined, and can be overridden in user code. + +--- + +### `void unicode_input_cancel(void)` {#api-unicode-input-cancel} + +Cancel the Unicode input sequence. The exact behavior depends on the currently selected input mode: + + - **macOS**: Release `UNICODE_KEY_MAC` + - **Linux**: Tap Escape + - **WinCompose**: Tap Escape + - **HexNumpad**: Release Left Alt + - **Emacs**: Tap Ctrl+G + +This function is weakly defined, and can be overridden in user code. + +--- + +### `void register_unicode(uint32_t code_point)` {#api-register-unicode} + +Input a single Unicode character. A surrogate pair will be sent if required by the input mode. + +#### Arguments {#api-register-unicode-arguments} + + - `uint32_t code_point` + The code point of the character to send. + +--- + +### `void send_unicode_string(const char *str)` {#api-send-unicode-string} + +Send a string containing Unicode characters. + +#### Arguments {#api-send-unicode-string-arguments} + + - `const char *str` + The string to send. + +--- + +### `uint8_t unicodemap_index(uint16_t keycode)` {#api-unicodemap-index} + +Get the index into the `unicode_map` array for the given keycode, respecting shift state for pair keycodes. + +#### Arguments {#api-unicodemap-index-arguments} + + - `uint16_t keycode` + The Unicode Map keycode to get the index of. + +#### Return Value {#api-unicodemap-index-return-value} + +An index into the `unicode_map` array. + +--- + +### `uint32_t unicodemap_get_code_point(uint8_t index)` {#api-unicodemap-get-code-point} + +Get the code point for the given index in the `unicode_map` array. + +#### Arguments {#unicodemap-get-code-point-arguments} + + - `uint8_t index` + The index into the `unicode_map` array. + +#### Return Value {#unicodemap-get-code-point-return-value} + +A Unicode code point value. + +--- + +### `void register_unicodemap(uint8_t index)` {#api-register-unicodemap} + +Send the code point for the given index in the `unicode_map` array. + +#### Arguments {#api-register-unicodemap-arguments} + + - `uint8_t index` + The index into the `unicode_map` array. + +--- + +### `void ucis_start(void)` {#api-ucis-start} + +Begin the input sequence. + +--- + +### `bool ucis_active(void)` {#api-ucis-active} + +Whether UCIS is currently active. + +#### Return Value {#api-ucis-active-return-value} + +`true` if UCIS is active. + +--- + +### `uint8_t ucis_count(void)` {#api-ucis-count} + +Get the number of characters in the input sequence buffer. + +#### Return Value {#api-ucis-count-return-value} + +The current input sequence buffer length. + +--- + +### `bool ucis_add(uint16_t keycode)` {#api-ucis-add} + +Add the given keycode to the input sequence buffer. + +#### Arguments {#api-ucis-add-arguments} + + - `uint16_t keycode` + The keycode to add. Must be between `KC_A` and `KC_Z`, or `KC_1` and `KC_0`. + +#### Return Value {#api-ucis-add-return-value} + +`true` if the keycode was added. + +--- + +### `bool ucis_remove_last(void)` {#api-ucis-remove-last} + +Remove the last character from the input sequence buffer. + +#### Return Value {#api-ucis-remove-last-return-value} + +`true` if the sequence was not empty. + +--- + +### `void ucis_finish(void)` {#api-ucis-finish} + +Mark the input sequence as complete, and attempt to match. + +--- + +### `void ucis_cancel(void)` {#api-ucis-cancel} + +Cancel the input sequence. + +--- + +### `void register_ucis(void)` {#api-register-ucis} + +Send the code point(s) for the given UCIS index. + +#### Arguments {#api-register-ucis-arguments} + + - `uint8_t index` + The index into the UCIS symbol table. diff --git a/docs/features/wpm.md b/docs/features/wpm.md new file mode 100644 index 0000000000..4f4ed43739 --- /dev/null +++ b/docs/features/wpm.md @@ -0,0 +1,76 @@ +# Word Per Minute (WPM) Calculation + +The WPM feature uses time between keystrokes to compute a rolling average words per minute rate and makes this available for various uses. + +Enable the WPM system by adding this to your `rules.mk`: + + WPM_ENABLE = yes + +For split keyboards using soft serial, the computed WPM score will be available on the master AND slave half. + +## Configuration + +| Define | Default | Description | +|------------------------------|---------------|------------------------------------------------------------------------------------------| +| `WPM_ESTIMATED_WORD_SIZE` | `5` | This is the value used when estimating average word size (for regression and normal use) | +| `WPM_ALLOW_COUNT_REGRESSION` | _Not defined_ | If defined allows the WPM to be decreased when hitting Delete or Backspace | +| `WPM_UNFILTERED` | _Not defined_ | If undefined (the default), WPM values will be smoothed to avoid sudden changes in value | +| `WPM_SAMPLE_SECONDS` | `5` | This defines how many seconds of typing to average, when calculating WPM | +| `WPM_SAMPLE_PERIODS` | `25` | This defines how many sampling periods to use when calculating WPM | +| `WPM_LAUNCH_CONTROL` | _Not defined_ | If defined, WPM values will be calculated using partial buffers when typing begins | + +'WPM_UNFILTERED' is potentially useful if you're filtering data in some other way (and also because it reduces the code required for the WPM feature), or if reducing measurement latency to a minimum is important for you. + +Increasing 'WPM_SAMPLE_SECONDS' will give more smoothly changing WPM values at the expense of slightly more latency to the WPM calculation. + +Increasing 'WPM_SAMPLE_PERIODS' will improve the smoothness at which WPM decays once typing stops, at a cost of approximately this many bytes of firmware space. + +If 'WPM_LAUNCH_CONTROL' is defined, whenever WPM drops to zero, the next time typing begins WPM will be calculated based only on the time since that typing began, instead of the whole period of time specified by WPM_SAMPLE_SECONDS. This results in reaching an accurate WPM value much faster, even when filtering is enabled and a large WPM_SAMPLE_SECONDS value is specified. + +## Public Functions + +|Function |Description | +|--------------------------|--------------------------------------------------| +|`get_current_wpm(void)` | Returns the current WPM as a value between 0-255 | +|`set_current_wpm(x)` | Sets the current WPM to `x` (between 0-255) | + +## Callbacks + +By default, the WPM score only includes letters, numbers, space and some punctuation. If you want to change the set of characters considered as part of the WPM calculation, you can implement your own `bool wpm_keycode_user(uint16_t keycode)` and return true for any characters you would like included in the calculation, or false to not count that particular keycode. + +For instance, the default is: + +```c +bool wpm_keycode_user(uint16_t keycode) { + if ((keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX) || (keycode >= QK_LAYER_TAP && keycode <= QK_LAYER_TAP_MAX) || (keycode >= QK_MODS && keycode <= QK_MODS_MAX)) { + keycode = keycode & 0xFF; + } else if (keycode > 0xFF) { + keycode = 0; + } + if ((keycode >= KC_A && keycode <= KC_0) || (keycode >= KC_TAB && keycode <= KC_SLSH)) { + return true; + } + + return false; +} +``` + +Additionally, if `WPM_ALLOW_COUNT_REGRESSION` is defined, there is the `uint8_t wpm_regress_count(uint16_t keycode)` function that allows you to decrease the WPM. This is useful if you want to be able to penalize certain keycodes (or even combinations). + +```c +__attribute__((weak)) uint8_t wpm_regress_count(uint16_t keycode) { + bool weak_modded = (keycode >= QK_LCTL && keycode < QK_LSFT) || (keycode >= QK_RCTL && keycode < QK_RSFT); + + if ((keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX) || (keycode >= QK_LAYER_TAP && keycode <= QK_LAYER_TAP_MAX) || (keycode >= QK_MODS && keycode <= QK_MODS_MAX)) { + keycode = keycode & 0xFF; + } else if (keycode > 0xFF) { + keycode = 0; + } + if (((get_mods() | get_oneshot_mods()) & MOD_MASK_CTRL} || weak_modded) && (keycode == KC_DEL || keycode == KC_BSPC)) { + return WPM_ESTIMATED_WORD_SIZE; + } + if (keycode == KC_DEL || keycode == KC_BSPC) { + return 1; + } +} +``` diff --git a/docs/flash_driver.md b/docs/flash_driver.md deleted file mode 100644 index 4160721350..0000000000 --- a/docs/flash_driver.md +++ /dev/null @@ -1,26 +0,0 @@ -# FLASH Driver Configuration {#flash-driver-configuration} - -The FLASH driver can be swapped out depending on the needs of the keyboard, or whether extra hardware is present. - -Driver | Description ------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -`FLASH_DRIVER = spi` | Supports writing to almost all NOR Flash chips. See the driver section below. - - -## SPI FLASH Driver Configuration {#spi-flash-driver-configuration} - -Currently QMK supports almost all NOR Flash chips over SPI. As such, requires a working spi_master driver configuration. You can override the driver configuration via your config.h: - -`config.h` override | Description | Default Value ------------------------------------------------|--------------------------------------------------------------------------------------|----------------- -`#define EXTERNAL_FLASH_SPI_SLAVE_SELECT_PIN` | SPI Slave select pin in order to inform that the FLASH is currently being addressed | _none_ -`#define EXTERNAL_FLASH_SPI_CLOCK_DIVISOR` | Clock divisor used to divide the peripheral clock to derive the SPI frequency | `8` -`#define EXTERNAL_FLASH_PAGE_SIZE` | The Page size of the FLASH in bytes, as specified in the datasheet | `256` -`#define EXTERNAL_FLASH_SECTOR_SIZE` | The sector size of the FLASH in bytes, as specified in the datasheet | `(4 * 1024)` -`#define EXTERNAL_FLASH_BLOCK_SIZE` | The block size of the FLASH in bytes, as specified in the datasheet | `(64 * 1024)` -`#define EXTERNAL_FLASH_SIZE` | The total size of the FLASH in bytes, as specified in the datasheet | `(512 * 1024)` -`#define EXTERNAL_FLASH_ADDRESS_SIZE` | The Flash address size in bytes, as specified in datasheet | `3` - -::: warning -All the above default configurations are based on MX25L4006E NOR Flash. -::: diff --git a/docs/flashing.md b/docs/flashing.md index c1e9f2a43d..798331eb23 100644 --- a/docs/flashing.md +++ b/docs/flashing.md @@ -53,7 +53,7 @@ QMK maintains [a fork of the LUFA DFU bootloader](https://github.com/qmk/lufa/tr //#define QMK_LED E6 //#define QMK_SPEAKER C6 ``` -Currently we do not recommend making `QMK_ESC` the same key as the one designated for [Bootmagic Lite](feature_bootmagic), as holding it down will cause the MCU to loop back and forth between entering and exiting the bootloader. +Currently we do not recommend making `QMK_ESC` the same key as the one designated for [Bootmagic Lite](features/bootmagic), as holding it down will cause the MCU to loop back and forth between entering and exiting the bootloader. The manufacturer and product strings are automatically pulled from `config.h`, with " Bootloader" appended to the product string. @@ -209,7 +209,7 @@ To enable the additional features, add the following defines to your `config.h`: //#define QMK_SPEAKER C6 ``` -Currently we do not recommend making `QMK_ESC` the same key as the one designated for [Bootmagic Lite](feature_bootmagic), as holding it down will cause the MCU to loop back and forth between entering and exiting the bootloader. +Currently we do not recommend making `QMK_ESC` the same key as the one designated for [Bootmagic Lite](features/bootmagic), as holding it down will cause the MCU to loop back and forth between entering and exiting the bootloader. The manufacturer and product strings are automatically pulled from `config.h`, with " Bootloader" appended to the product string. diff --git a/docs/getting_started_make_guide.md b/docs/getting_started_make_guide.md index 8a66a65c21..59455adb30 100644 --- a/docs/getting_started_make_guide.md +++ b/docs/getting_started_make_guide.md @@ -115,11 +115,11 @@ This allows you to send Unicode characters using `UM()` in your keyma This allows you to send Unicode characters by inputting a mnemonic corresponding to the character you want to send. You will need to maintain a mapping table in your keymap file. All possible code points (up to `0x10FFFF`) are supported. -For further details, as well as limitations, see the [Unicode page](feature_unicode). +For further details, as well as limitations, see the [Unicode page](features/unicode). `AUDIO_ENABLE` -This allows you output audio on the C6 pin (needs abstracting). See the [audio page](feature_audio) for more information. +This allows you output audio on the C6 pin (needs abstracting). See the [audio page](features/audio) for more information. `VARIABLE_TRACE` @@ -127,7 +127,7 @@ Use this to debug changes to variable values, see the [tracing variables](unit_t `KEY_LOCK_ENABLE` -This enables [key lock](feature_key_lock). +This enables [key lock](features/key_lock). `SPLIT_KEYBOARD` diff --git a/docs/gpio_control.md b/docs/gpio_control.md deleted file mode 100644 index 9ce4f2aa20..0000000000 --- a/docs/gpio_control.md +++ /dev/null @@ -1,44 +0,0 @@ -# GPIO Control {#gpio-control} - -QMK has a GPIO control abstraction layer which is microcontroller agnostic. This is done to allow easy access to pin control across different platforms. - -## Macros {#macros} - -The following macros provide basic control of GPIOs and are found in `platforms//gpio.h`. - -|Macro |Description | -|-------------------------------------|---------------------------------------------------------------------| -|`gpio_set_pin_input(pin)` |Set pin as input with high impedance (High-Z) | -|`gpio_set_pin_input_high(pin)` |Set pin as input with builtin pull-up resistor | -|`gpio_set_pin_input_low(pin)` |Set pin as input with builtin pull-down resistor (unavailable on AVR)| -|`gpio_set_pin_output(pin)` |Set pin as output (alias of `gpio_set_pin_output_push_pull`) | -|`gpio_set_pin_output_push_pull(pin)` |Set pin as output, push/pull mode | -|`gpio_set_pin_output_open_drain(pin)`|Set pin as output, open-drain mode (unavailable on AVR and ATSAM) | -|`gpio_write_pin_high(pin)` |Set pin level as high, assuming it is an output | -|`gpio_write_pin_low(pin)` |Set pin level as low, assuming it is an output | -|`gpio_write_pin(pin, level)` |Set pin level, assuming it is an output | -|`gpio_read_pin(pin)` |Returns the level of the pin | -|`gpio_toggle_pin(pin)` |Invert pin level, assuming it is an output | - -## Advanced Settings {#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. For AVR, the standard `avr/io.h` library is used; for STM32, the ChibiOS [PAL library](https://chibios.sourceforge.net/docs3/hal/group___p_a_l.html) is used. - -## Atomic Operation {#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(void) { - // 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/hand_wire.md b/docs/hand_wire.md index 0928888f0e..cbb9179060 100644 --- a/docs/hand_wire.md +++ b/docs/hand_wire.md @@ -88,7 +88,7 @@ Note that these methods can be combined. Prepare your lengths of wire before mo ### A note on split keyboards -If you are planning a split keyboard (e.g. Dactyl) each half will require a controller and a means of communicating between them (like a TRRS or hardwired cable). Further information can be found in the [QMK split keyboard documentation.](feature_split_keyboard) +If you are planning a split keyboard (e.g. Dactyl) each half will require a controller and a means of communicating between them (like a TRRS or hardwired cable). Further information can be found in the [QMK split keyboard documentation.](features/split_keyboard) ### Soldering diff --git a/docs/hardware_drivers.md b/docs/hardware_drivers.md index 6960bbcaa1..694d46971a 100644 --- a/docs/hardware_drivers.md +++ b/docs/hardware_drivers.md @@ -16,20 +16,20 @@ Support for addressing pins on the ProMicro by their Arduino name rather than th ## SSD1306 OLED Driver -Support for SSD1306 based OLED displays. For more information see the [OLED Driver Feature](feature_oled_driver) page. +Support for SSD1306 based OLED displays. For more information see the [OLED Driver Feature](features/oled_driver) page. ## WS2812 -Support for WS2811/WS2812{a,b,c} LED's. For more information see the [RGB Light](feature_rgblight) page. +Support for WS2811/WS2812{a,b,c} LED's. For more information see the [RGB Light](features/rgblight) page. ## IS31FL3731 -Support for up to 2 drivers. Each driver impliments 2 charlieplex matrices to individually address LEDs using I2C. This allows up to 144 same color LEDs or 32 RGB LEDs. For more information on how to setup the driver see the [RGB Matrix](feature_rgb_matrix) page. +Support for up to 2 drivers. Each driver impliments 2 charlieplex matrices to individually address LEDs using I2C. This allows up to 144 same color LEDs or 32 RGB LEDs. For more information on how to setup the driver see the [RGB Matrix](features/rgb_matrix) page. ## IS31FL3733 -Support for up to a single driver with room for expansion. Each driver can control 192 individual LEDs or 64 RGB LEDs. For more information on how to setup the driver see the [RGB Matrix](feature_rgb_matrix) page. +Support for up to a single driver with room for expansion. Each driver can control 192 individual LEDs or 64 RGB LEDs. For more information on how to setup the driver see the [RGB Matrix](features/rgb_matrix) page. ## 24xx series external I2C EEPROM -Support for an external I2C-based EEPROM instead of using the on-chip EEPROM. For more information on how to setup the driver see the [EEPROM Driver](eeprom_driver) page. +Support for an external I2C-based EEPROM instead of using the on-chip EEPROM. For more information on how to setup the driver see the [EEPROM Driver](drivers/eeprom) page. diff --git a/docs/hardware_keyboard_guidelines.md b/docs/hardware_keyboard_guidelines.md index 728e09c8a9..de67fa3bc3 100644 --- a/docs/hardware_keyboard_guidelines.md +++ b/docs/hardware_keyboard_guidelines.md @@ -230,7 +230,7 @@ Given the amount of functionality that QMK exposes it's very easy to confuse new ### Magic Keycodes and Command -[Magic Keycodes](keycodes_magic) and [Command](feature_command) are two related features that allow a user to control their keyboard in non-obvious ways. We recommend you think long and hard about if you're going to enable either feature, and how you will expose this functionality. Keep in mind that users who want this functionality can enable it in their personal keymaps without affecting all the novice users who may be using your keyboard as their first programmable board. +[Magic Keycodes](keycodes_magic) and [Command](features/command) are two related features that allow a user to control their keyboard in non-obvious ways. We recommend you think long and hard about if you're going to enable either feature, and how you will expose this functionality. Keep in mind that users who want this functionality can enable it in their personal keymaps without affecting all the novice users who may be using your keyboard as their first programmable board. By far the most common problem new users encounter is accidentally triggering Bootmagic while they're plugging in their keyboard. They're holding the keyboard by the bottom, unknowingly pressing in alt and spacebar, and then they find that these keys have been swapped on them. We recommend leaving this feature disabled by default, but if you do turn it on consider setting `BOOTMAGIC_KEY_SALT` to a key that is hard to press while plugging your keyboard in. diff --git a/docs/i2c_driver.md b/docs/i2c_driver.md deleted file mode 100644 index ccc21137b3..0000000000 --- a/docs/i2c_driver.md +++ /dev/null @@ -1,290 +0,0 @@ -# I2C Master Driver {#i2c-master-driver} - -The I2C Master drivers used in QMK have a set of common functions to allow portability between MCUs. - -## Usage {#usage} - -In most cases, the I2C Master driver code is automatically included if you are using a feature or driver which requires it, such as [OLED](feature_oled_driver). - -However, if you need to use the driver standalone, add the following to your `rules.mk`: - -```make -I2C_DRIVER_REQUIRED = yes -``` - -You can then call the I2C API by including `i2c_master.h` in your code. - -## I2C Addressing {#note-on-i2c-addresses} - -All of the addresses expected by this driver should be pushed to the upper 7 bits of the address byte. Setting -the lower bit (indicating read/write) will be done by the respective functions. Almost all I2C addresses listed -on datasheets and the internet will be represented as 7 bits occupying the lower 7 bits and will need to be -shifted to the left (more significant) by one bit. This is easy to do via the bitwise shift operator `<< 1`. - -You can either do this on each call to the functions below, or once in your definition of the address. For example, if your device has an address of `0x18`: - -```c -#define MY_I2C_ADDRESS (0x18 << 1) -``` - -See https://www.robot-electronics.co.uk/i2c-tutorial for more information about I2C addressing and other technical details. - -## AVR Configuration {#avr-configuration} - -The following defines can be used to configure the I2C master driver: - -|`config.h` Override|Description |Default | -|-------------------|---------------------|--------| -|`F_SCL` |Clock frequency in Hz|`400000`| - -No further setup is required - just connect the `SDA` and `SCL` pins of your I2C devices to the matching pins on the MCU: - -|MCU |`SCL`|`SDA`| -|------------------|-----|-----| -|ATmega16/32U4 |`D0` |`D1` | -|AT90USB64/128 |`D0` |`D1` | -|ATmega32A |`C0` |`C1` | -|ATmega328/P |`C5` |`C4` | - -::: tip -The ATmega16/32U2 does not possess I2C functionality, and so cannot use this driver. -::: - -## ChibiOS/ARM Configuration {#arm-configuration} - -You'll need to determine which pins can be used for I2C -- a an example, STM32 parts generally have multiple I2C peripherals, labeled I2C1, I2C2, I2C3 etc. - -To enable I2C, modify your board's `halconf.h` to enable I2C: - -```c -#define HAL_USE_I2C TRUE -``` - -Then, modify your board's `mcuconf.h` to enable the peripheral you've chosen, for example: - -```c -#undef STM32_I2C_USE_I2C2 -#define STM32_I2C_USE_I2C2 TRUE -``` - -|`mcuconf.h` Setting |Description |Default| -|----------------------------|----------------------------------------------------------------------------------|-------| -|`STM32_I2C_BUSY_TIMEOUT` |Time in milliseconds until the I2C command is aborted if no response is received |`50` | -|`STM32_I2C_XXX_IRQ_PRIORITY`|Interrupt priority for hardware driver XXX (THIS IS AN EXPERT SETTING) |`10` | -|`STM32_I2C_USE_DMA` |Enable/Disable the ability of the MCU to offload the data transfer to the DMA unit|`TRUE` | -|`STM32_I2C_XXX_DMA_PRIORITY`|Priority of DMA unit for hardware driver XXX (THIS IS AN EXPERT SETTING) |`1` | - -Configuration-wise, you'll need to set up the peripheral as per your MCU's datasheet -- the defaults match the pins for a Proton-C, i.e. STM32F303. - -|`config.h` Overrride |Description |Default| -|------------------------|--------------------------------------------------------------|-------| -|`I2C_DRIVER` |I2C peripheral to use - I2C1 -> `I2CD1`, I2C2 -> `I2CD2` etc. |`I2CD1`| -|`I2C1_SCL_PIN` |The pin definition for SCL |`B6` | -|`I2C1_SCL_PAL_MODE` |The alternate function mode for SCL |`4` | -|`I2C1_SDA_PIN` |The pin definition for SDA |`B7` | -|`I2C1_SDA_PAL_MODE` |The alternate function mode for SDA |`4` | - -The following configuration values depend on the specific MCU in use. - -### I2Cv1 {#arm-configuration-i2cv1} - -* STM32F1xx -* STM32F2xx -* STM32F4xx -* STM32L0xx -* STM32L1xx - -See [this page](https://www.playembedded.org/blog/stm32-i2c-chibios/#7_I2Cv1_configuration_structure) for the I2Cv1 configuration structure. - -|`config.h` Override|Default | -|-------------------|----------------| -|`I2C1_OPMODE` |`OPMODE_I2C` | -|`I2C1_CLOCK_SPEED` |`100000` | -|`I2C1_DUTY_CYCLE` |`STD_DUTY_CYCLE`| - -### I2Cv2 {#arm-configuration-i2cv2} - -* STM32F0xx -* STM32F3xx -* STM32F7xx -* STM32L4xx - -See [this page](https://www.playembedded.org/blog/stm32-i2c-chibios/#8_I2Cv2_I2Cv3_configuration_structure) for the I2Cv2 configuration structure. - -|`config.h` Override |Default| -|---------------------|-------| -|`I2C1_TIMINGR_PRESC` |`0U` | -|`I2C1_TIMINGR_SCLDEL`|`7U` | -|`I2C1_TIMINGR_SDADEL`|`0U` | -|`I2C1_TIMINGR_SCLH` |`38U` | -|`I2C1_TIMINGR_SCLL` |`129U` | - -## API {#api} - -### `void i2c_init(void)` {#api-i2c-init} - -Initialize the I2C driver. This function must be called only once, before any of the below functions can be called. - -This function is weakly defined, meaning it can be overridden if necessary for your particular use case: - -```c -void i2c_init(void) { - gpio_set_pin_input(B6); // Try releasing special pins for a short time - gpio_set_pin_input(B7); - wait_ms(10); // Wait for the release to happen - - palSetPadMode(GPIOB, 6, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_PUPDR_PULLUP); // Set B6 to I2C function - palSetPadMode(GPIOB, 7, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_PUPDR_PULLUP); // Set B7 to I2C function -} -``` - ---- - -### `i2c_status_t i2c_transmit(uint8_t address, uint8_t *data, uint16_t length, uint16_t timeout)` {#api-i2c-transmit} - -Send multiple bytes to the selected I2C device. - -#### Arguments {#api-i2c-transmit-arguments} - - - `uint8_t address` - The 7-bit I2C address of the device. - - `uint8_t *data` - A pointer to the data to transmit. - - `uint16_t length` - The number of bytes to write. Take care not to overrun the length of `data`. - - `uint16_t timeout` - The time in milliseconds to wait for a response from the target device. - -#### Return Value {#api-i2c-transmit-return} - -`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`. - ---- - -### `i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-receive} - -Receive multiple bytes from the selected I2C device. - -#### Arguments {#api-i2c-receive-arguments} - - - `uint8_t address` - The 7-bit I2C address of the device. - - `uint8_t *data` - A pointer to the buffer to read into. - - `uint16_t length` - The number of bytes to read. Take care not to overrun the length of `data`. - - `uint16_t timeout` - The time in milliseconds to wait for a response from the target device. - -#### Return Value {#api-i2c-receive-return} - -`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`. - ---- - -### `i2c_status_t i2c_write_register(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-write-register} - -Writes to a register with an 8-bit address on the I2C device. - -#### Arguments {#api-i2c-write-register-arguments} - - - `uint8_t devaddr` - The 7-bit I2C address of the device. - - `uint8_t regaddr` - The register address to write to. - - `uint8_t *data` - A pointer to the data to transmit. - - `uint16_t length` - The number of bytes to write. Take care not to overrun the length of `data`. - - `uint16_t timeout` - The time in milliseconds to wait for a response from the target device. - -#### Return Value {#api-i2c-write-register-return} - -`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`. - ---- - -### `i2c_status_t i2c_write_register16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-write-register16} - -Writes to a register with a 16-bit address (big endian) on the I2C device. - -#### Arguments {#api-i2c-write-register16-arguments} - - - `uint8_t devaddr` - The 7-bit I2C address of the device. - - `uint16_t regaddr` - The register address to write to. - - `uint8_t *data` - A pointer to the data to transmit. - - `uint16_t length` - The number of bytes to write. Take care not to overrun the length of `data`. - - `uint16_t timeout` - The time in milliseconds to wait for a response from the target device. - -#### Return Value {#api-i2c-write-register16-return} - -`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`. - ---- - -### `i2c_status_t i2c_read_register(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-read-register} - -Reads from a register with an 8-bit address on the I2C device. - -#### Arguments {#api-i2c-read-register-arguments} - - - `uint8_t devaddr` - The 7-bit I2C address of the device. - - `uint8_t regaddr` - The register address to read from. - - `uint16_t length` - The number of bytes to read. Take care not to overrun the length of `data`. - - `uint16_t timeout` - The time in milliseconds to wait for a response from the target device. - -#### Return Value {#api-i2c-read-register-return} - -`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`. - ---- - -### `i2c_status_t i2c_read_register16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-read-register16} - -Reads from a register with a 16-bit address (big endian) on the I2C device. - -#### Arguments {#api-i2c-read-register16-arguments} - - - `uint8_t devaddr` - The 7-bit I2C address of the device. - - `uint16_t regaddr` - The register address to read from. - - `uint16_t length` - The number of bytes to read. Take care not to overrun the length of `data`. - - `uint16_t timeout` - The time in milliseconds to wait for a response from the target device. - -#### Return Value {#api-i2c-read-register16-return} - -`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`. - ---- - -### `i2c_status_t i2c_ping_address(uint8_t address, uint16_t timeout)` {#api-i2c-ping-address} - -Pings the I2C bus for a specific address. - -On ChibiOS a "best effort" attempt is made by reading a single byte from register 0 at the requested address. This should generally work except for I2C devices that do not not respond to a register 0 read request, which will result in a false negative result (unsucessful response to ping attempt). - -This function is weakly defined, meaning it can be overridden if necessary for your particular use case: - -#### Arguments - - - `uint8_t address` - The 7-bit I2C address of the device (ie. without the read/write bit - this will be set automatically). - - `uint16_t timeout` - The time in milliseconds to wait for a response from the target device. - -#### Return Value - -`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`. diff --git a/docs/keycodes.md b/docs/keycodes.md index 38ed5ab18d..4c91d98fa7 100644 --- a/docs/keycodes.md +++ b/docs/keycodes.md @@ -233,7 +233,7 @@ See also: [Quantum Keycodes](quantum_keycodes#qmk-keycodes) ## Audio Keys {#audio-keys} -See also: [Audio](feature_audio) +See also: [Audio](features/audio) |Key |Aliases |Description | |-------------------------|---------|-------------------------------------------| @@ -255,7 +255,7 @@ See also: [Audio](feature_audio) ## Auto Shift {#auto-shift} -See also: [Auto Shift](feature_auto_shift) +See also: [Auto Shift](features/auto_shift) |Key |Aliases |Description | |----------------------|---------|--------------------------------------------| @@ -268,7 +268,7 @@ See also: [Auto Shift](feature_auto_shift) ## Autocorrect {#autocorrect} -See also: [Autocorrect](feature_autocorrect) +See also: [Autocorrect](features/autocorrect) |Key |Aliases |Description | |-----------------------|---------|----------------------------------------------| @@ -278,7 +278,7 @@ See also: [Autocorrect](feature_autocorrect) ## Backlighting {#backlighting} -See also: [Backlighting](feature_backlight) +See also: [Backlighting](features/backlight) | Key | Aliases | Description | |---------------------------------|-----------|-------------------------------------| @@ -292,7 +292,7 @@ See also: [Backlighting](feature_backlight) ## Bluetooth {#bluetooth} -See also: [Bluetooth](feature_bluetooth) +See also: [Bluetooth](features/bluetooth) |Key |Aliases |Description | |---------------------|---------|----------------------------------------------| @@ -302,7 +302,7 @@ See also: [Bluetooth](feature_bluetooth) ## Caps Word {#caps-word} -See also: [Caps Word](feature_caps_word) +See also: [Caps Word](features/caps_word) |Key |Aliases |Description | |---------------------|---------|------------------------------| @@ -310,7 +310,7 @@ See also: [Caps Word](feature_caps_word) ## Dynamic Macros {#dynamic-macros} -See also: [Dynamic Macros](feature_dynamic_macros) +See also: [Dynamic Macros](features/dynamic_macros) |Key |Aliases |Description | |---------------------------------|---------|--------------------------------------------------| @@ -322,7 +322,7 @@ See also: [Dynamic Macros](feature_dynamic_macros) ## Grave Escape {#grave-escape} -See also: [Grave Escape](feature_grave_esc) +See also: [Grave Escape](features/grave_esc) |Key |Aliases |Description | |-----------------|---------|------------------------------------------------------------------| @@ -330,7 +330,7 @@ See also: [Grave Escape](feature_grave_esc) ## Joystick {#joystick} -See also: [Joystick](feature_joystick) +See also: [Joystick](features/joystick) |Key |Aliases|Description| |-----------------------|-------|-----------| @@ -369,7 +369,7 @@ See also: [Joystick](feature_joystick) ## Key Lock {#key-lock} -See also: [Key Lock](feature_key_lock) +See also: [Key Lock](features/key_lock) |Key |Description | |---------|--------------------------------------------------------------| @@ -392,7 +392,7 @@ See also: [Layer Switching](feature_layers#switching-and-toggling-layers) ## Leader Key {#leader-key} -See also: [Leader Key](feature_leader_key) +See also: [Leader Key](features/leader_key) |Key |Description | |---------|------------------------| @@ -400,7 +400,7 @@ See also: [Leader Key](feature_leader_key) ## LED Matrix {#led-matrix} -See also: [LED Matrix](feature_led_matrix) +See also: [LED Matrix](features/led_matrix) |Key |Aliases |Description | |-------------------------------|---------|-----------------------------------| @@ -458,7 +458,7 @@ See also: [Magic Keycodes](keycodes_magic) ## MIDI {#midi} -See also: [MIDI](feature_midi) +See also: [MIDI](features/midi) |Key |Aliases |Description | |-------------------------------|------------------|---------------------------------| @@ -609,7 +609,7 @@ See also: [MIDI](feature_midi) ## Mouse Keys {#mouse-keys} -See also: [Mouse Keys](feature_mouse_keys) +See also: [Mouse Keys](features/mouse_keys) |Key |Aliases |Description | |----------------|---------|---------------------------| @@ -699,7 +699,7 @@ See also: [Dynamic Tapping Term](tap_hold#dynamic-tapping-term) ## RGB Lighting {#rgb-lighting} -See also: [RGB Lighting](feature_rgblight) +See also: [RGB Lighting](features/rgblight) |Key |Aliases |Description | |-------------------|----------|--------------------------------------------------------------------| @@ -724,7 +724,7 @@ See also: [RGB Lighting](feature_rgblight) ## RGB Matrix Lighting {#rgb-matrix-lighting} -See also: [RGB Matrix Lighting](feature_rgb_matrix) +See also: [RGB Matrix Lighting](features/rgb_matrix) |Key |Aliases |Description | |-------------------|----------|--------------------------------------------------------------------------------------| @@ -782,7 +782,7 @@ See also: [One Shot Keys](one_shot_keys) ## Programmable Button Support {#programmable-button} -See also: [Programmable Button](feature_programmable_button) +See also: [Programmable Button](features/programmable_button) |Key |Aliases|Description | |---------------------------|-------|----------------------| @@ -821,7 +821,7 @@ See also: [Programmable Button](feature_programmable_button) ## Repeat Key {#repeat-key} -See also: [Repeat Key](feature_repeat_key) +See also: [Repeat Key](features/repeat_key) |Keycode |Aliases |Description | |-----------------------|---------|-------------------------------------| @@ -830,7 +830,7 @@ See also: [Repeat Key](feature_repeat_key) ## Space Cadet {#space-cadet} -See also: [Space Cadet](feature_space_cadet) +See also: [Space Cadet](features/space_cadet) |Key |Aliases |Description | |----------------------------------------------|---------|----------------------------------------| @@ -844,7 +844,7 @@ See also: [Space Cadet](feature_space_cadet) ## Swap Hands {#swap-hands} -See also: [Swap Hands](feature_swap_hands) +See also: [Swap Hands](features/swap_hands) |Key |Aliases |Description | |-----------------------------|---------|----------------------------------------------------| @@ -859,7 +859,7 @@ See also: [Swap Hands](feature_swap_hands) ## Unicode Support {#unicode-support} -See also: [Unicode Support](feature_unicode) +See also: [Unicode Support](features/unicode) |Key |Aliases |Description | |----------------------------|---------|----------------------------------------------------------------| diff --git a/docs/mod_tap.md b/docs/mod_tap.md index 0008967c52..37c2ba3473 100644 --- a/docs/mod_tap.md +++ b/docs/mod_tap.md @@ -55,7 +55,7 @@ For convenience, QMK includes some Mod-Tap shortcuts to make common combinations Currently, the `kc` argument of `MT()` is limited to the [Basic Keycode set](keycodes_basic), meaning you can't use keycodes like `LCTL()`, `KC_TILD`, or anything greater than `0xFF`. This is because QMK uses 16-bit keycodes, of which 3 bits are used for the function identifier, 1 bit for selecting right or left mods, and 4 bits to tell which mods are used, leaving only 8 bits for the keycode. Additionally, if at least one right-handed modifier is specified in a Mod-Tap, it will cause all modifiers specified to become right-handed, so it is not possible to mix and match the two - for example, Left Control and Right Shift would become Right Control and Right Shift. -Expanding this would be complicated, at best. Moving to a 32-bit keycode would solve a lot of this, but would double the amount of space that the keymap matrix uses. And it could potentially cause issues, too. If you need to apply modifiers to your tapped keycode, [Tap Dance](feature_tap_dance#example-5-using-tap-dance-for-advanced-mod-tap-and-layer-tap-keys) can be used to accomplish this. +Expanding this would be complicated, at best. Moving to a 32-bit keycode would solve a lot of this, but would double the amount of space that the keymap matrix uses. And it could potentially cause issues, too. If you need to apply modifiers to your tapped keycode, [Tap Dance](features/tap_dance#example-5-using-tap-dance-for-advanced-mod-tap-and-layer-tap-keys) can be used to accomplish this. You may also run into issues when using Remote Desktop Connection on Windows. Because these keycodes send key events faster than a human, Remote Desktop could miss them. To fix this, open Remote Desktop Connection, click on "Show Options", open the "Local Resources" tab, and in the keyboard section, change the drop down to "On this Computer". This will fix the issue, and allow the characters to work correctly. diff --git a/docs/newbs_building_firmware.md b/docs/newbs_building_firmware.md index d7c1157b2d..f3afd6a896 100644 --- a/docs/newbs_building_firmware.md +++ b/docs/newbs_building_firmware.md @@ -64,8 +64,8 @@ How to complete this step is entirely up to you. Make the one change that's been * [Basic Keycodes](keycodes_basic) * [Quantum Keycodes](quantum_keycodes) -* [Grave/Escape](feature_grave_esc) -* [Mouse keys](feature_mouse_keys) +* [Grave/Escape](features/grave_esc) +* [Mouse keys](features/mouse_keys) ::: tip While you get a feel for how keymaps work, keep each change small. Bigger changes make it harder to debug any problems that arise. diff --git a/docs/platformdev_rp2040.md b/docs/platformdev_rp2040.md index f0b006cf6e..1269ffeeb5 100644 --- a/docs/platformdev_rp2040.md +++ b/docs/platformdev_rp2040.md @@ -4,16 +4,16 @@ The following table shows the current driver status for peripherals on RP2040 MC | System | Support | | ---------------------------------------------------------------- | ---------------------------------------------- | -| [ADC driver](adc_driver) | :heavy_check_mark: | -| [Audio](audio_driver#pwm-hardware) | :heavy_check_mark: | -| [Backlight](feature_backlight) | :heavy_check_mark: | -| [I2C driver](i2c_driver) | :heavy_check_mark: | -| [SPI driver](spi_driver) | :heavy_check_mark: | -| [WS2812 driver](ws2812_driver) | :heavy_check_mark: using `PIO` driver | -| [External EEPROMs](eeprom_driver) | :heavy_check_mark: using `I2C` or `SPI` driver | -| [EEPROM emulation](eeprom_driver#wear_leveling-configuration) | :heavy_check_mark: | -| [serial driver](serial_driver) | :heavy_check_mark: using `SIO` or `PIO` driver | -| [UART driver](uart_driver) | :heavy_check_mark: using `SIO` driver | +| [ADC driver](drivers/adc) | :heavy_check_mark: | +| [Audio](drivers/audio#pwm-hardware) | :heavy_check_mark: | +| [Backlight](features/backlight) | :heavy_check_mark: | +| [I2C driver](drivers/i2c) | :heavy_check_mark: | +| [SPI driver](drivers/spi) | :heavy_check_mark: | +| [WS2812 driver](drivers/ws2812) | :heavy_check_mark: using `PIO` driver | +| [External EEPROMs](drivers/eeprom) | :heavy_check_mark: using `I2C` or `SPI` driver | +| [EEPROM emulation](drivers/eeprom#wear_leveling-configuration) | :heavy_check_mark: | +| [serial driver](drivers/serial) | :heavy_check_mark: using `SIO` or `PIO` driver | +| [UART driver](drivers/uart) | :heavy_check_mark: using `SIO` driver | ## GPIO @@ -43,7 +43,7 @@ QMK RP2040 support builds upon ChibiOS and thus follows their convention for act | `I2C0` | `RP_I2C_USE_I2C0` | `I2CD0` | | `I2C1` | `RP_I2C_USE_I2C1` | `I2CD1` | -To configure the I2C driver please read the [ChibiOS/ARM](i2c_driver#arm-configuration) section. +To configure the I2C driver please read the [ChibiOS/ARM](drivers/i2c#arm-configuration) section. ### SPI Driver @@ -52,7 +52,7 @@ To configure the I2C driver please read the [ChibiOS/ARM](i2c_driver#arm-configu | `SPI0` | `RP_SPI_USE_SPI0` | `SPID0` | | `SPI1` | `RP_SPI_USE_SPI1` | `SPID1` | -To configure the SPI driver please read the [ChibiOS/ARM](spi_driver#chibiosarm-configuration) section. +To configure the SPI driver please read the [ChibiOS/ARM](drivers/spi#chibiosarm-configuration) section. ### UART Driver @@ -92,7 +92,7 @@ This is the default board that is chosen, unless any other RP2040 board is selec | `SPI_MISO_PIN` | `GP20` | | `SPI_MOSI_PIN` | `GP19` | | **Serial driver** | | -| `SERIAL_USART_DRIVER` ([SIO Driver](serial_driver#the-sio-driver) only) | `SIOD0` | +| `SERIAL_USART_DRIVER` ([SIO Driver](drivers/serial#the-sio-driver) only) | `SIOD0` | | `SOFT_SERIAL_PIN` | undefined, use `SERIAL_USART_TX_PIN` | | `SERIAL_USART_TX_PIN` | `GP0` | | `SERIAL_USART_RX_PIN` | `GP1` | @@ -115,9 +115,9 @@ BOARD = GENERIC_RP_RP2040 ## Split keyboard support -Split keyboards are fully supported using the [serial driver](serial_driver) in both full-duplex and half-duplex configurations. Two driver subsystems are supported by the RP2040, the hardware UART based `SIO` and the Programmable IO based `PIO` driver. +Split keyboards are fully supported using the [serial driver](drivers/serial) in both full-duplex and half-duplex configurations. Two driver subsystems are supported by the RP2040, the hardware UART based `SIO` and the Programmable IO based `PIO` driver. -| Feature | [SIO Driver](serial_driver#the-sio-driver) | [PIO Driver](serial_driver#the-pio-driver) | +| Feature | [SIO Driver](drivers/serial#the-sio-driver) | [PIO Driver](drivers/serial#the-pio-driver) | | ----------------------------- | --------------------------------------------- | --------------------------------------------- | | Half-Duplex operation | | :heavy_check_mark: | | Full-Duplex operation | :heavy_check_mark: | :heavy_check_mark: | diff --git a/docs/porting_your_keyboard_to_qmk.md b/docs/porting_your_keyboard_to_qmk.md index c91e5ca31d..eb45790128 100644 --- a/docs/porting_your_keyboard_to_qmk.md +++ b/docs/porting_your_keyboard_to_qmk.md @@ -153,7 +153,7 @@ In the above example, * `"matrix": [0, 0]` defines the electrical position ::: tip -See also: [Split Keyboard Layout Macro](feature_split_keyboard#layout-macro) and [Matrix to Physical Layout](understanding_qmk#matrix-to-physical-layout-map). +See also: [Split Keyboard Layout Macro](features/split_keyboard#layout-macro) and [Matrix to Physical Layout](understanding_qmk#matrix-to-physical-layout-map). ::: ## Additional Configuration diff --git a/docs/pr_checklist.md b/docs/pr_checklist.md index e5ed1d67b6..f7b16e1d85 100644 --- a/docs/pr_checklist.md +++ b/docs/pr_checklist.md @@ -126,7 +126,7 @@ https://github.com/qmk/qmk_firmware/pulls?q=is%3Apr+is%3Aclosed+label%3Akeyboard - commented-out functions removed too - `matrix_init_board()` etc. migrated to `keyboard_pre_init_kb()`, see: [keyboard_pre_init*](custom_quantum_functions#keyboard_pre_init_-function-documentation) - prefer `CUSTOM_MATRIX = lite` if custom matrix used, allows for standard debounce, see [custom matrix 'lite'](custom_matrix#lite) - - prefer LED indicator [Configuration Options](feature_led_indicators#configuration-options) to custom `led_update_*()` implementations where possible + - prefer LED indicator [Configuration Options](features/led_indicators#configuration-options) to custom `led_update_*()` implementations where possible - hardware that's enabled at the keyboard level and requires configuration such as OLED displays or encoders should have basic functionality implemented here - `.h` - `#include "quantum.h"` appears at the top @@ -135,12 +135,12 @@ https://github.com/qmk/qmk_firmware/pulls?q=is%3Apr+is%3Aclosed+label%3Akeyboard - no duplication of `rules.mk` or `config.h` from keyboard - `keymaps/default/keymap.c` - `QMKBEST`/`QMKURL` example macros removed - - if using `MO(1)` and `MO(2)` keycodes together to access a third layer, the [Tri Layer](feature_tri_layer) feature should be used, rather than manually implementing this using `layer_on/off()` and `update_tri_layer()` functions in the keymap's `process_record_user()`. + - if using `MO(1)` and `MO(2)` keycodes together to access a third layer, the [Tri Layer](features/tri_layer) feature should be used, rather than manually implementing this using `layer_on/off()` and `update_tri_layer()` functions in the keymap's `process_record_user()`. - default (and via) keymaps should be "pristine" - bare minimum to be used as a "clean slate" for another user to develop their own user-specific keymap - what does pristine mean? no custom keycodes. no advanced features like tap dance or macros. basic mod taps and home row mods would be acceptable where their use is necessary - standard layouts preferred in these keymaps, if possible - - should use [encoder map feature](feature_encoders#encoder-map), rather than `encoder_update_user()` + - should use [encoder map feature](features/encoders#encoder-map), rather than `encoder_update_user()` - default keymap should not enable VIA -- the VIA integration documentation requires a keymap called `via` - submitters can add an example (or bells-and-whistles) keymap showcasing capabilities in the same PR but it shouldn't be embedded in the 'default' keymap - submitters can also have a "manufacturer-matching" keymap that mirrors existing functionality of the commercial product, if porting an existing board diff --git a/docs/ref_functions.md b/docs/ref_functions.md index 156c9ed20b..577273c05d 100644 --- a/docs/ref_functions.md +++ b/docs/ref_functions.md @@ -71,7 +71,7 @@ Do you want to set the default layer, so that it's retained even after you unplu To use this, you would use `set_single_persistent_default_layer(layer)`. If you have a name defined for your layer, you can use that instead (such as _QWERTY, _DVORAK or _COLEMAK). -This will set the default layer, update the persistent settings, and play a tune if you have [Audio](feature_audio) enabled on your board, and the default layer sounds set. +This will set the default layer, update the persistent settings, and play a tune if you have [Audio](features/audio) enabled on your board, and the default layer sounds set. To configure the default layer sounds, you would want to define this in your `config.h` file, like this: @@ -99,7 +99,7 @@ To reset to the bootloader use `QK_BOOTLOADER` or `QK_BOOT` keycode or `reset_ke ## Wiping the EEPROM (Persistent Storage) -If you're having issues with Audio, RGB Underglow, backlighting or keys acting weird, then you can reset the EEPROM (persistent setting storage). To force an EEPROM reset, use the [`EE_CLR` keycode](quantum_keycodes) or [Bootmagic Lite](feature_bootmagic) functionality. If neither of those are an option, then you can use a custom macro to do so. +If you're having issues with Audio, RGB Underglow, backlighting or keys acting weird, then you can reset the EEPROM (persistent setting storage). To force an EEPROM reset, use the [`EE_CLR` keycode](quantum_keycodes) or [Bootmagic Lite](features/bootmagic) functionality. If neither of those are an option, then you can use a custom macro to do so. To wipe the EEPROM, run `eeconfig_init()` from your function or macro to reset most of the settings to default. diff --git a/docs/reference_glossary.md b/docs/reference_glossary.md index cf8c8f0d75..d24576b333 100644 --- a/docs/reference_glossary.md +++ b/docs/reference_glossary.md @@ -36,7 +36,7 @@ An alternative keyboard layout developed by Dr. August Dvorak in the 1930's. A s ## Dynamic Macro A macro which has been recorded on the keyboard and which will be lost when the keyboard is unplugged or the computer rebooted. -* [Dynamic Macro Documentation](feature_dynamic_macros) +* [Dynamic Macro Documentation](features/dynamic_macros) ## Eclipse An IDE that is popular with many C developers. @@ -76,7 +76,7 @@ An abstraction used to allow a key to serve multiple purposes. The highest activ ## Leader Key A feature that allows you to tap the leader key followed by a sequence of 1, 2, or 3 keys to activate key presses or other quantum features. -* [Leader Key Documentation](feature_leader_key) +* [Leader Key Documentation](features/leader_key) ## LED Light Emitting Diode, the most common device used for indicators on a keyboard. @@ -101,7 +101,7 @@ A key that is held down while typing another key to modify the action of that ke ## Mousekeys A feature that lets you control your mouse cursor and click from your keyboard. -* [Mousekeys Documentation](feature_mouse_keys) +* [Mousekeys Documentation](features/mouse_keys) ## N-Key Rollover (NKRO) A term that applies to keyboards that are capable of reporting any number of key-presses at once. @@ -130,7 +130,7 @@ A 1 byte number that is sent as part of a HID report over USB that represents a ## Space Cadet Shift A special set of shift keys which allow you to type various types of braces by tapping the left or right shift one or more times. -* [Space Cadet Shift Documentation](feature_space_cadet) +* [Space Cadet Shift Documentation](features/space_cadet) ## Tap Pressing and releasing a key. In some situations you will need to distinguish between a key down and a key up event, and Tap always refers to both at once. @@ -138,7 +138,7 @@ Pressing and releasing a key. In some situations you will need to distinguish be ## Tap Dance A feature that lets you assign multiple keycodes to the same key based on how many times you press it. -* [Tap Dance Documentation](feature_tap_dance) +* [Tap Dance Documentation](features/tap_dance) ## Teensy A low-cost AVR development board that is commonly used for hand-wired builds. A teensy is often chosen despite costing a few dollars more due to its halfkay bootloader, which makes flashing very simple. @@ -149,7 +149,7 @@ A generic term for LEDs that light the underside of the board. These LEDs typica ## Unicode In the larger computer world Unicode is a set of encoding schemes for representing characters in any language. As it relates to QMK it means using various OS schemes to send unicode codepoints instead of scancodes. -* [Unicode Documentation](feature_unicode) +* [Unicode Documentation](features/unicode) ## Unit Testing A framework for running automated tests against QMK. Unit testing helps us be confident that our changes do not break anything. diff --git a/docs/reference_info_json.md b/docs/reference_info_json.md index 25d2e9d1d8..2db2cd1427 100644 --- a/docs/reference_info_json.md +++ b/docs/reference_info_json.md @@ -95,7 +95,7 @@ You can create `info.json` files at every level under `qmk_firmware/keyboards/ - -## Bitbang - -This is the Default driver, absence of configuration assumes this driver. It works by [bit banging](https://en.wikipedia.org/wiki/Bit_banging) a GPIO pin using the CPU. It is therefore not as efficient as a dedicated hardware peripheral, which the Half-duplex and Full-duplex drivers use. - -::: warning -On ARM platforms the bitbang driver causes connection issues when using it together with the bitbang WS2812 driver. Choosing alternate drivers for both serial and WS2812 (instead of bitbang) is strongly recommended. -::: - -### Pin configuration - -``` - LEFT RIGHT -+-------+ SERIAL +-------+ -| SSP |-----------------| SSP | -| | VDD | | -| |-----------------| | -| | GND | | -| |-----------------| | -+-------+ +-------+ -``` - -One GPIO pin is needed for the bitbang driver, as only one wire is used for receiving and transmitting data. This pin is referred to as the `SOFT_SERIAL_PIN` (SSP) in the configuration. A TRS or USB cable provides enough conductors for this driver to function. - -### Setup - -To use the bitbang driver follow these steps to activate it. - -1. Change the `SERIAL_DRIVER` to `bitbang` in your keyboards `rules.mk` file: - -```make -SERIAL_DRIVER = bitbang -``` - -2. Configure the GPIO pin of your keyboard via the `config.h` file: - -```c -#define SOFT_SERIAL_PIN D0 // or D1, D2, D3, E6 -``` - -3. On ARM platforms you must turn on ChibiOS `PAL_USE_CALLBACKS` feature: - -* In `halconf.h` add the line `#define PAL_USE_CALLBACKS TRUE`. - -
- -## USART Half-duplex - -Targeting ARM boards based on ChibiOS, where communication is offloaded to a USART hardware device that supports Half-duplex operation. The advantages over bitbanging are fast, accurate timings and reduced CPU usage. Therefore it is advised to choose Half-duplex over Bitbang if MCU is capable of utilising Half-duplex, and Full-duplex can't be used instead (e.g. lack of available GPIO pins, or imcompatible PCB design). - -### Pin configuration - -``` - LEFT RIGHT -+-------+ | | +-------+ -| | R R | | -| | | SERIAL | | | -| TX |-----------------| TX | -| | VDD | | -| |-----------------| | -| | GND | | -| |-----------------| | -+-------+ +-------+ -``` - -Only one GPIO pin is needed for the Half-duplex driver, as only one wire is used for receiving and transmitting data. This pin is referred to as the `SERIAL_USART_TX_PIN` in the configuration. Ensure that the pin chosen for split communication can operate as the TX pin of the contoller's USART peripheral. A TRS or USB cable provides enough conductors for this driver to function. As the split connection is configured to operate in open-drain mode, an **external pull-up resistor is needed to keep the line high**. Resistor values of 1.5kΩ to 8.2kΩ are known to work. - -::: warning -***Note:*** A pull-up resistor isn't required for RP2040 controllers configured with PIO subsystem. -::: - -### Setup - -To use the Half-duplex driver follow these steps to activate it. If you target the Raspberry Pi RP2040 PIO implementation, start at step 2. - -1. Change the `SERIAL_DRIVER` to `usart` in your keyboards `rules.mk` file: - -```make -SERIAL_DRIVER = usart -``` - -Skip to step 3. - -2. (RP2040 + PIO only!) Change the `SERIAL_DRIVER` to `vendor` in your keyboards `rules.mk` file: - -```make -SERIAL_DRIVER = vendor -``` - -3. Configure the hardware of your keyboard via the `config.h` file: - -```c -#define SERIAL_USART_TX_PIN B6 // The GPIO pin that is used split communication. -``` - -For STM32 MCUs several GPIO configuration options can be changed as well. See the section ["Alternate Functions for selected STM32 MCUs"](#alternate-functions-for-selected-stm32-mcus). - -```c -#define USART1_REMAP // Remap USART TX and RX pins on STM32F103 MCUs, see table below. -#define SERIAL_USART_TX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7 -``` - -4. Decide either for `SERIAL`, `SIO`, or `PIO` subsystem. See section ["Choosing a driver subsystem"](#choosing-a-driver-subsystem). - -
- -## USART Full-duplex - -Targeting ARM boards based on ChibiOS where communication is offloaded to an USART hardware device. The advantages over bitbanging are fast, accurate timings and reduced CPU usage; therefore it is advised to choose this driver over all others where possible. Due to its internal design Full-duplex is slightly more efficient than the Half-duplex driver, but Full-duplex should be primarily chosen if Half-duplex operation is not supported by the controller's USART peripheral. - -### Pin configuration - -``` - LEFT RIGHT -+-------+ +-------+ -| | SERIAL | | -| TX |-----------------| RX | -| | SERIAL | | -| RX |-----------------| TX | -| | VDD | | -| |-----------------| | -| | GND | | -| |-----------------| | -+-------+ +-------+ -``` - -Two GPIO pins are needed for the Full-duplex driver, as two distinct wires are used for receiving and transmitting data. The pin transmitting data is the `TX` pin and refereed to as the `SERIAL_USART_TX_PIN`, the pin receiving data is the `RX` pin and refereed to as the `SERIAL_USART_RX_PIN` in this configuration. Please note that `TX` pin of the master half has to be connected with the `RX` pin of the slave half and the `RX` pin of the master half has to be connected with the `TX` pin of the slave half! Usually this pin swap has to be done outside of the MCU e.g. with cables or on the PCB. Some MCUs like the STM32F303 used on the Proton-C allow this pin swap directly inside the MCU. A TRRS or USB cable provides enough conductors for this driver to function. - -To use this driver the USART peripherals `TX` and `RX` pins must be configured with the correct Alternate-functions. If you are using a Proton-C development board everything is already setup, same is true for STM32F103 MCUs. For MCUs which are using a modern flexible GPIO configuration you have to specify these by setting `SERIAL_USART_TX_PAL_MODE` and `SERIAL_USART_RX_PAL_MODE`. Refer to the corresponding datasheets of your MCU or find those settings in the section ["Alternate Functions for selected STM32 MCUs"](#alternate-functions-for-selected-stm32-mcus). - -### Setup - -To use the Full-duplex driver follow these steps to activate it. If you target the Raspberry Pi RP2040 PIO implementation, start at step 2 - -1. Change the `SERIAL_DRIVER` to `usart` in your keyboards `rules.mk` file: - -```make -SERIAL_DRIVER = usart -``` - -Skip to step 3 - -2. (RP2040 + PIO only!) Change the `SERIAL_DRIVER` to `vendor` in your keyboards `rules.mk` file: - -```make -SERIAL_DRIVER = vendor -``` - -3. Configure the hardware of your keyboard via the `config.h` file: - -```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 -``` - -For STM32 MCUs several GPIO configuration options, including the ability for `TX` to `RX` pin swapping, can be changed as well. See the section ["Alternate Functions for selected STM32 MCUs"](#alternate-functions-for-selected-stm32-mcus). - -```c -#define SERIAL_USART_PIN_SWAP // Swap TX and RX pins if keyboard is master halve. (Only available on some MCUs) -#define USART1_REMAP // Remap USART TX and RX pins on STM32F103 MCUs, see table below. -#define SERIAL_USART_TX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7 -``` - -4. Decide either for `SERIAL`, `SIO`, or `PIO` subsystem. See section ["Choosing a driver subsystem"](#choosing-a-driver-subsystem). - -
- -## Choosing a driver subsystem - -### The `SERIAL` driver - -The `SERIAL` Subsystem is supported for the majority of ChibiOS MCUs and should be used whenever supported. Follow these steps in order to activate it: - -1. In your keyboards `halconf.h` add: - -```c -#define HAL_USE_SERIAL TRUE -``` - -2. In your keyboards `mcuconf.h`: activate the USART peripheral that is used on your MCU. The shown example is for an STM32 MCU, so this will not work on MCUs by other manufacturers. You can find the correct names in the `mcuconf.h` files of your MCU that ship with ChibiOS. - -Just below `#include_next ` add: - -```c -#include_next - -#undef STM32_SERIAL_USE_USARTn -#define STM32_SERIAL_USE_USARTn TRUE -``` - -Where 'n' matches the peripheral number of your selected USART on the MCU. - -3. In you keyboards `config.h`: override the default USART `SERIAL` driver if you use a USART peripheral that does not belong to the default selected `SD1` driver. For instance, if you selected `STM32_SERIAL_USE_USART3` the matching driver would be `SD3`. - -```c - #define SERIAL_USART_DRIVER SD3 - ``` - -### The `SIO` driver - -The `SIO` Subsystem was added to ChibiOS with the 21.11 release and is only supported on selected MCUs. It should only be chosen when the `SERIAL` subsystem is not supported by your MCU. - -Follow these steps in order to activate it: - -1. In your keyboards `halconf.h` add: - -```c -#define HAL_USE_SIO TRUE -``` - -2. In your keyboards `mcuconf.h:` activate the USART peripheral that is used on your MCU. The shown example is for an STM32 MCU, so this will not work on MCUs by other manufacturers. You can find the correct names in the `mcuconf.h` files of your MCU that ship with ChibiOS. - -Just below `#include_next ` add: - -```c -#include_next - -#undef STM32_SIO_USE_USARTn -#define STM32_SIO_USE_USARTn TRUE -``` - -Where 'n' matches the peripheral number of your selected USART on the MCU. - -3. In the keyboard's `config.h` file: override the default USART `SIO` driver if you use a USART peripheral that does not belong to the default selected `SIOD1` driver. For instance, if you selected `STM32_SERIAL_USE_USART3` the matching driver would be `SIOD3`. - -```c - #define SERIAL_USART_DRIVER SIOD3 - ``` - -### The `PIO` driver - -The `PIO` subsystem is a Raspberry Pi RP2040 specific implementation, using an integrated PIO peripheral and is therefore only available on this MCU. Because of the flexible nature of PIO peripherals, **any** GPIO pin can be used as a `TX` or `RX` pin. Half-duplex and Full-duplex operation modes are fully supported with this driver. Half-duplex uses the built-in pull-ups and GPIO manipulation of the RP2040 to drive the line high by default, thus an external pull-up resistor **is not required**. - -Optionally, the PIO peripheral utilized for split communication can be changed with the following define in config.h: -```c -#define SERIAL_PIO_USE_PIO1 // Force the usage of PIO1 peripheral, by default the Serial implementation uses the PIO0 peripheral -``` - -The Serial PIO program uses 2 state machines, 13 instructions and the complete interrupt handler of the PIO peripheral it is running on. - -
- -## Advanced Configuration - -There are several advanced configuration options that can be defined in your keyboards `config.h` file: - -### Baudrate - -If you're having issues or need a higher baudrate with serial communication, you can change the baudrate which in turn controls the communication speed for serial. You want to lower the baudrate if you experience failed transactions. - -```c -#define SELECT_SOFT_SERIAL_SPEED {#} -``` - -| Speed | Bitbang | Half-duplex and Full-duplex | -| ----- | -------------------------- | --------------------------- | -| `0` | 189000 baud (experimental) | 460800 baud | -| `1` | 137000 baud (default) | 230400 baud (default) | -| `2` | 75000 baud | 115200 baud | -| `3` | 39000 baud | 57600 baud | -| `4` | 26000 baud | 38400 baud | -| `5` | 20000 baud | 19200 baud | - -Alternatively you can specify the baudrate directly by defining `SERIAL_USART_SPEED`. - -### Timeout - -This is the default time window in milliseconds in which a successful communication has to complete. Usually you don't want to change this value. But you can do so anyways by defining an alternate one in your keyboards `config.h` file: - -```c -#define SERIAL_USART_TIMEOUT 20 // USART driver timeout. default 20 -``` - -
- -## Troubleshooting - -If you're having issues withe serial communication, you can enable debug messages that will give you insights which part of the communication failed. The enable these messages add to your keyboards `config.h` file: - -```c -#define SERIAL_DEBUG -``` - -::: tip -The messages will be printed out to the `CONSOLE` output. For additional information, refer to [Debugging/Troubleshooting QMK](faq_debug). -::: - -## Alternate Functions for selected STM32 MCUs - -Pins for USART Peripherals with - -### STM32F303 / Proton-C [Datasheet](https://www.st.com/resource/en/datasheet/stm32f303cc.pdf) - -Pin Swap available: :heavy_check_mark: - -| Pin | Function | Mode | -| ---------- | -------- | ---- | -| **USART1** | | | -| PA9 | TX | AF7 | -| PA10 | RX | AF7 | -| PB6 | TX | AF7 | -| PB7 | RX | AF7 | -| PC4 | TX | AF7 | -| PC5 | RX | AF7 | -| PE0 | TX | AF7 | -| PE1 | RX | AF7 | -| **USART2** | | | -| PA2 | TX | AF7 | -| PA3 | RX | AF7 | -| PA14 | TX | AF7 | -| PA15 | RX | AF7 | -| PB3 | TX | AF7 | -| PB4 | RX | AF7 | -| PD5 | TX | AF7 | -| PD6 | RX | AF7 | -| **USART3** | | | -| PB10 | TX | AF7 | -| PB11 | RX | AF7 | -| PC10 | TX | AF7 | -| PC11 | RX | AF7 | -| PD8 | TX | AF7 | -| PD9 | RX | AF7 | - -### STM32F072 [Datasheet](https://www.st.com/resource/en/datasheet/stm32f072c8.pdf) - -Pin Swap available: :heavy_check_mark: - -| Pin | Function | Mode | -| ------ | -------- | ---- | -| USART1 | | | -| PA9 | TX | AF1 | -| PA10 | RX | AF1 | -| PB6 | TX | AF0 | -| PB7 | RX | AF0 | -| USART2 | | | -| PA2 | TX | AF1 | -| PA3 | RX | AF1 | -| PA14 | TX | AF1 | -| PA15 | RX | AF1 | -| USART3 | | | -| PB10 | TX | AF4 | -| PB11 | RX | AF4 | -| PC4 | TX | AF1 | -| PC5 | RX | AF1 | -| PC10 | TX | AF1 | -| PC11 | RX | AF1 | -| PD8 | TX | AF0 | -| PD9 | RX | AF0 | -| USART4 | | | -| PA0 | TX | AF4 | -| PA1 | RX | AF4 | - -### STM32F103 Medium Density (C8-CB) [Datasheet](https://www.st.com/resource/en/datasheet/stm32f103c8.pdf) - -Pin Swap available: N/A - -TX Pin is always Alternate Function Push-Pull, RX Pin is always regular input pin for any USART peripheral. **For STM32F103 no additional Alternate Function configuration is necessary. QMK is already configured.** - -Pin remapping: - -The pins of USART Peripherals use default Pins that can be remapped to use other pins using the AFIO registers. Default pins are marked **bold**. Add the appropriate defines to your config.h file. - -| Pin | Function | Mode | USART_REMAP | -| ---------- | -------- | ---- | ------------------- | -| **USART1** | | | | -| **PA9** | TX | AFPP | | -| **PA10** | RX | IN | | -| PB6 | TX | AFPP | USART1_REMAP | -| PB7 | RX | IN | USART1_REMAP | -| **USART2** | | | | -| **PA2** | TX | AFPP | | -| **PA3** | RX | IN | | -| PD5 | TX | AFPP | USART2_REMAP | -| PD6 | RX | IN | USART2_REMAP | -| **USART3** | | | | -| **PB10** | TX | AFPP | | -| **PB11** | RX | IN | | -| PC10 | TX | AFPP | USART3_PARTIALREMAP | -| PC11 | RX | IN | USART3_PARTIALREMAP | -| PD8 | TX | AFPP | USART3_FULLREMAP | -| PD9 | RX | IN | USART3_FULLREMAP | diff --git a/docs/spi_driver.md b/docs/spi_driver.md deleted file mode 100644 index b77e2dbb46..0000000000 --- a/docs/spi_driver.md +++ /dev/null @@ -1,167 +0,0 @@ -# SPI Master Driver {#spi-master-driver} - -The SPI Master drivers used in QMK have a set of common functions to allow portability between MCUs. - -## Usage {#usage} - -In most cases, the SPI Master driver code is automatically included if you are using a feature or driver which requires it, such as [OLED](feature_oled_driver). - -However, if you need to use the driver standalone, add the following to your `rules.mk`: - -```make -SPI_DRIVER_REQUIRED = yes -``` - -You can then call the SPI API by including `spi_master.h` in your code. - -## AVR Configuration {#avr-configuration} - -No special setup is required - just connect the `SS`, `SCK`, `MOSI` and `MISO` pins of your SPI devices to the matching pins on the MCU: - -|MCU |`SS`|`SCK`|`MOSI`|`MISO`| -|-----------------|----|-----|------|------| -|ATmega16/32U2/4 |`B0`|`B1` |`B2` |`B3` | -|AT90USB64/128/162|`B0`|`B1` |`B2` |`B3` | -|ATmega32A |`B4`|`B7` |`B5` |`B6` | -|ATmega328/P |`B2`|`B5` |`B3` |`B4` | - -You may use more than one slave select pin, not just the `SS` pin. This is useful when you have multiple devices connected and need to communicate with them individually. -`SPI_SS_PIN` can be passed to `spi_start()` to refer to `SS`. - -## ChibiOS/ARM Configuration {#arm-configuration} - -You'll need to determine which pins can be used for SPI -- as an example, STM32 parts generally have multiple SPI peripherals, labeled SPI1, SPI2, SPI3 etc. - -To enable SPI, modify your board's `halconf.h` to enable SPI: - -```c -#define HAL_USE_SPI TRUE -#define SPI_USE_WAIT TRUE -#define SPI_SELECT_MODE SPI_SELECT_MODE_PAD -``` - -Then, modify your board's `mcuconf.h` to enable the peripheral you've chosen, for example: - -```c -#undef STM32_SPI_USE_SPI2 -#define STM32_SPI_USE_SPI2 TRUE -``` - -Configuration-wise, you'll need to set up the peripheral as per your MCU's datasheet -- the defaults match the pins for a Proton-C, i.e. STM32F303. - -|`config.h` Override|Description |Default| -|-------------------|-------------------------------------------------------------|-------| -|`SPI_DRIVER` |SPI peripheral to use - SPI1 -> `SPID1`, SPI2 -> `SPID2` etc.|`SPID2`| -|`SPI_SCK_PIN` |The pin to use for SCK |`B13` | -|`SPI_SCK_PAL_MODE` |The alternate function mode for SCK |`5` | -|`SPI_MOSI_PIN` |The pin to use for MOSI |`B15` | -|`SPI_MOSI_PAL_MODE`|The alternate function mode for MOSI |`5` | -|`SPI_MISO_PIN` |The pin to use for MISO |`B14` | -|`SPI_MISO_PAL_MODE`|The alternate function mode for MISO |`5` | - -As per the AVR configuration, you may choose any other standard GPIO as a slave select pin, which should be supplied to `spi_start()`. - -If a complete SPI interface is not required, then the following can be done to disable certain SPI pins, so they don't occupy a GPIO unnecessarily: - - in `config.h`: `#define SPI_MISO_PIN NO_PIN` - - in `config.h`: `#define SPI_MOSI_PIN NO_PIN` - - in `mcuconf.h`: `#define SPI_SELECT_MODE SPI_SELECT_MODE_NONE`, in this case the `slavePin` argument passed to `spi_start()` may be `NO_PIN` if the slave select pin is not used. - -## API {#api} - -### `void spi_init(void)` {#api-spi-init} - -Initialize the SPI driver. This function must be called only once, before any of the below functions can be called. - ---- - -### `bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor)` {#api-spi-start} - -Start an SPI transaction. - -#### Arguments {#api-spi-start-arguments} - - - `pin_t slavePin` - The QMK pin to assert as the slave select pin, eg. `B4`. - - `bool lsbFirst` - Determines the endianness of the transmission. If `true`, the least significant bit of each byte is sent first. - - `uint8_t mode` - The SPI mode to use: - - |Mode|Clock Polarity |Clock Phase | - |----|--------------------|-----------------------| - |`0` |Leading edge rising |Sample on leading edge | - |`1` |Leading edge rising |Sample on trailing edge| - |`2` |Leading edge falling|Sample on leading edge | - |`3` |Leading edge falling|Sample on trailing edge| - - - `uint16_t divisor` - The SPI clock divisor, will be rounded up to the nearest power of two. This number can be calculated by dividing the MCU's clock speed by the desired SPI clock speed. For example, an MCU running at 8 MHz wanting to talk to an SPI device at 4 MHz would set the divisor to `2`. - -#### Return Value {#api-spi-start-return} - -`false` if the supplied parameters are invalid or the SPI peripheral is already in use, or `true`. - ---- - -### `spi_status_t spi_write(uint8_t data)` {#api-spi-write} - -Write a byte to the selected SPI device. - -#### Arguments {#api-spi-write-arguments} - - - `uint8_t data` - The byte to write. - -#### Return Value {#api-spi-write-return} - -`SPI_STATUS_TIMEOUT` if the timeout period elapses, or `SPI_STATUS_SUCCESS`. - ---- - -### `spi_status_t spi_read(void)` {#api-spi-read} - -Read a byte from the selected SPI device. - -#### Return Value {#api-spi-read-return} - -`SPI_STATUS_TIMEOUT` if the timeout period elapses, or the byte read from the device. - ---- - -### `spi_status_t spi_transmit(const uint8_t *data, uint16_t length)` {#api-spi-transmit} - -Send multiple bytes to the selected SPI device. - -#### Arguments {#api-spi-transmit-arguments} - - - `const uint8_t *data` - A pointer to the data to write from. - - `uint16_t length` - The number of bytes to write. Take care not to overrun the length of `data`. - -#### Return Value {#api-spi-transmit-return} - -`SPI_STATUS_TIMEOUT` if the timeout period elapses, `SPI_STATUS_ERROR` if some other error occurs, otherwise `SPI_STATUS_SUCCESS`. - ---- - -### `spi_status_t spi_receive(uint8_t *data, uint16_t length)` {#api-spi-receive} - -Receive multiple bytes from the selected SPI device. - -#### Arguments {#api-spi-receive-arguments} - - - `uint8_t *data` - A pointer to the buffer to read into. - - `uint16_t length` - The number of bytes to read. Take care not to overrun the length of `data`. - -#### Return Value {#api-spi-receive-return} - -`SPI_STATUS_TIMEOUT` if the timeout period elapses, `SPI_STATUS_ERROR` if some other error occurs, otherwise `SPI_STATUS_SUCCESS`. - ---- - -### `void spi_stop(void)` {#api-spi-stop} - -End the current SPI transaction. This will deassert the slave select pin and reset the endianness, mode and divisor configured by `spi_start()`. diff --git a/docs/syllabus.md b/docs/syllabus.md index 82b6110c80..3082f1ebd5 100644 --- a/docs/syllabus.md +++ b/docs/syllabus.md @@ -24,24 +24,24 @@ These topics start to dig into some of the features that QMK supports. You don't * **Learn How To Configure Features** - * [Audio](feature_audio) + * [Audio](features/audio) * Lighting - * [Backlight](feature_backlight) - * [LED Matrix](feature_led_matrix) - * [RGB Lighting](feature_rgblight) - * [RGB Matrix](feature_rgb_matrix) + * [Backlight](features/backlight) + * [LED Matrix](features/led_matrix) + * [RGB Lighting](features/rgblight) + * [RGB Matrix](features/rgb_matrix) * [Tap-Hold Configuration](tap_hold) * [Squeezing Space from AVR](squeezing_avr) * **Learn More About Keymaps** * [Keymaps](keymap) * [Custom Functions and Keycodes](custom_quantum_functions) * Macros - * [Dynamic Macros](feature_dynamic_macros) + * [Dynamic Macros](features/dynamic_macros) * [Compiled Macros](feature_macros) - * [Tap Dance](feature_tap_dance) - * [Combos](feature_combo) + * [Tap Dance](features/tap_dance) + * [Combos](features/combo) * [Userspace](feature_userspace) - * [Key Overrides](feature_key_overrides) + * [Key Overrides](features/key_overrides) # Advanced Topics @@ -53,15 +53,15 @@ Everything below here requires a lot of foundational knowledge. Besides being ab * [info.json Reference](reference_info_json) * [Debounce API](feature_debounce_type) * **Advanced Features** - * [Unicode](feature_unicode) + * [Unicode](features/unicode) * [API](api_overview) - * [Bootmagic Lite](feature_bootmagic) + * [Bootmagic Lite](features/bootmagic) * **Hardware** * [How Keyboards Work](how_keyboards_work) * [How A Keyboard Matrix Works](how_a_matrix_works) - * [Split Keyboards](feature_split_keyboard) - * [Stenography](feature_stenography) - * [Pointing Devices](feature_pointing_device) + * [Split Keyboards](features/split_keyboard) + * [Stenography](features/stenography) + * [Pointing Devices](features/pointing_device) * **Core Development** * [Coding Conventions](coding_conventions_c) * [Compatible Microcontrollers](compatible_microcontrollers) diff --git a/docs/tap_hold.md b/docs/tap_hold.md index fe862894b4..9b7f6552cb 100644 --- a/docs/tap_hold.md +++ b/docs/tap_hold.md @@ -497,7 +497,7 @@ Do not use `MOD_xxx` constants like `MOD_LSFT` or `MOD_RALT`, since they're 5-bi ### Retro Shift -[Auto Shift,](feature_auto_shift) has its own version of `retro tapping` called `retro shift`. It is extremely similar to `retro tapping`, but holding the key past `AUTO_SHIFT_TIMEOUT` results in the value it sends being shifted. Other configurations also affect it differently; see [here](feature_auto_shift#retro-shift) for more information. +[Auto Shift,](features/auto_shift) has its own version of `retro tapping` called `retro shift`. It is extremely similar to `retro tapping`, but holding the key past `AUTO_SHIFT_TIMEOUT` results in the value it sends being shifted. Other configurations also affect it differently; see [here](features/auto_shift#retro-shift) for more information. ## Why do we include the key record for the per key functions? diff --git a/docs/uart_driver.md b/docs/uart_driver.md deleted file mode 100644 index 23f5b3d6e4..0000000000 --- a/docs/uart_driver.md +++ /dev/null @@ -1,122 +0,0 @@ -# UART Driver {#uart-driver} - -The UART drivers used in QMK have a set of common functions to allow portability between MCUs. - -Currently, this driver does not support enabling hardware flow control (the `RTS` and `CTS` pins) if available, but may do so in future. - -## Usage {#usage} - -In most cases, the UART driver code is automatically included if you are using a feature or driver which requires it. - -However, if you need to use the driver standalone, add the following to your `rules.mk`: - -```make -UART_DRIVER_REQUIRED = yes -``` - -You can then call the UART API by including `uart.h` in your code. - -## AVR Configuration {#avr-configuration} - -No special setup is required - just connect the `RX` and `TX` pins of your UART device to the opposite pins on the MCU: - -|MCU |`TX`|`RX`|`CTS`|`RTS`| -|-------------|----|----|-----|-----| -|ATmega16/32U2|`D3`|`D2`|`D7` |`D6` | -|ATmega16/32U4|`D3`|`D2`|`D5` |`B7` | -|AT90USB64/128|`D3`|`D2`|*n/a*|*n/a*| -|ATmega32A |`D1`|`D0`|*n/a*|*n/a*| -|ATmega328/P |`D1`|`D0`|*n/a*|*n/a*| - -## ChibiOS/ARM Configuration {#arm-configuration} - -You'll need to determine which pins can be used for UART -- as an example, STM32 parts generally have multiple UART peripherals, labeled USART1, USART2, USART3 etc. - -To enable UART, modify your board's `mcuconf.h` to enable the peripheral you've chosen, for example: - -```c -#undef STM32_SERIAL_USE_USART2 -#define STM32_SERIAL_USE_USART2 TRUE -``` - -Configuration-wise, you'll need to set up the peripheral as per your MCU's datasheet -- the defaults match the pins for a Proton-C, i.e. STM32F303. - -| `config.h` override | Description | Default Value | -| --------------------------- | --------------------------------------------------------------- | ------------- | -| `#define UART_DRIVER` | USART peripheral to use - USART1 -> `SD1`, USART2 -> `SD2` etc. | `SD1` | -| `#define UART_TX_PIN` | The pin to use for TX | `A9` | -| `#define UART_TX_PAL_MODE` | The alternate function mode for TX | `7` | -| `#define UART_RX_PIN` | The pin to use for RX | `A10` | -| `#define UART_RX_PAL_MODE` | The alternate function mode for RX | `7` | -| `#define UART_CTS_PIN` | The pin to use for CTS | `A11` | -| `#define UART_CTS_PAL_MODE` | The alternate function mode for CTS | `7` | -| `#define UART_RTS_PIN` | The pin to use for RTS | `A12` | -| `#define UART_RTS_PAL_MODE` | The alternate function mode for RTS | `7` | - -## API {#api} - -### `void uart_init(uint32_t baud)` {#api-uart-init} - -Initialize the UART driver. This function must be called only once, before any of the below functions can be called. - -#### Arguments {#api-uart-init-arguments} - - - `uint32_t baud` - The baud rate to transmit and receive at. This may depend on the device you are communicating with. Common values are 1200, 2400, 4800, 9600, 19200, 38400, 57600, and 115200. - ---- - -### `void uart_write(uint8_t data)` {#api-uart-write} - -Transmit a single byte. - -#### Arguments {#api-uart-write-arguments} - - - `uint8_t data` - The byte to write. - ---- - -### `uint8_t uart_read(void)` {#api-uart-read} - -Receive a single byte. - -#### Return Value {#api-uart-read-return} - -The byte read from the receive buffer. This function will block if the buffer is empty (ie. no data to read). - ---- - -### `void uart_transmit(const uint8_t *data, uint16_t length)` {#api-uart-transmit} - -Transmit multiple bytes. - -#### Arguments {#api-uart-transmit-arguments} - - - `const uint8_t *data` - A pointer to the data to write from. - - `uint16_t length` - The number of bytes to write. Take care not to overrun the length of `data`. - ---- - -### `void uart_receive(char *data, uint16_t length)` {#api-uart-receive} - -Receive multiple bytes. - -#### Arguments {#api-uart-receive-arguments} - - - `uint8_t *data` - A pointer to the buffer to read into. - - `uint16_t length` - The number of bytes to read. Take care not to overrun the length of `data`. - ---- - -### `bool uart_available(void)` {#api-uart-available} - -Return whether the receive buffer contains data. Call this function to determine if `uart_read()` will return data immediately. - -#### Return Value {#api-uart-available-return} - -`true` if the receive buffer length is non-zero. diff --git a/docs/ws2812_driver.md b/docs/ws2812_driver.md deleted file mode 100644 index c445e2dbf6..0000000000 --- a/docs/ws2812_driver.md +++ /dev/null @@ -1,253 +0,0 @@ -# WS2812 Driver {#ws2812-driver} - -This driver provides support for WorldSemi addressable RGB(W) LEDs, and compatible equivalents: - - * WS2811, WS2812, WS2812B, WS2812C, etc. - * SK6812, SK6812MINI, SK6805 - -These LEDs are often called "addressable" because instead of using a wire per color (and per LED), each LED contains a small microchip that understands a special protocol sent over a single wire. -The LEDs can be chained together, and the remaining data is passed on to the next. In this way, you can easily control the color of many LEDs using a single GPIO. - -## Usage {#usage} - -In most cases, the WS2812 driver code is automatically included if you are using either the [RGBLight](feature_rgblight) or [RGB Matrix](feature_rgb_matrix) feature with the `ws2812` driver set, and you would use those APIs instead. - -However, if you need to use the driver standalone, add the following to your `rules.mk`: - -```make -WS2812_DRIVER_REQUIRED = yes -``` - -You can then call the WS2812 API by including `ws2812.h` in your code. - -## Basic Configuration {#basic-configuration} - -Add the following to your `config.h`: - -|Define |Default |Description | -|-------------------|-----------------------|------------------------------------------------------------------------------------------------| -|`WS2812_DI_PIN` |*Not defined* |The GPIO pin connected to the DI pin of the first LED in the chain | -|`WS2812_LED_COUNT` |*Not defined* |Number of LEDs in the WS2812 chain - automatically set when RGBLight or RGB Matrix is configured| -|`WS2812_TIMING` |`1250` |The total length of a bit (TH+TL) in nanoseconds | -|`WS2812_T1H` |`900` |The length of a "1" bit's high phase in nanoseconds | -|`WS2812_T0H` |`350` |The length of a "0" bit's high phase in nanoseconds | -|`WS2812_TRST_US` |`280` |The length of the reset phase in microseconds | -|`WS2812_BYTE_ORDER`|`WS2812_BYTE_ORDER_GRB`|The byte order of the RGB data | -|`WS2812_RGBW` |*Not defined* |Enables RGBW support (except `i2c` driver) | - -### Timing Adjustment {#timing-adjustment} - -The WS2812 LED communication protocol works by encoding a "1" bit with a long high pulse (T1H), and a "0" bit with a shorter pulse (T0H). The total cycle length of a bit is the same. -The "reset" pulse (TRST) latches the sent RGB data to all of the LEDs and denotes a completed "frame". - -Some WS2812 variants have slightly different timing parameter requirements, which can be accounted for if necessary using the above `#define`s in your `config.h`. - -### Byte Order {#byte-order} - -Some WS2812 variants may have their color components in a different physical or logical order. For example, the WS2812B-2020 has physically swapped red and green LEDs, which causes the wrong color to be displayed, because the default order of the bytes sent over the wire is defined as GRB. -If you find your LED colors are consistently swapped, you may need to change the byte order by adding the following to your `config.h`: - -```c -#define WS2812_BYTE_ORDER WS2812_BYTE_ORDER_GRB -``` - -Where the byte order may be one of: - -|Byte Order|Known Devices | -|----------|----------------------------| -|`GRB` |Most WS2812s, SK6812, SK6805| -|`RGB` |WS2812B-2020 | -|`BGR` |TM1812 | - -### RGBW Support {#rgbw-support} - -Rendering the color white with RGB LEDs is typically inconsistent due to inherent variations between each individual LED die. However, some WS2812 variants (such as SK6812RGBW) also possess a white LED along with the red, green, and blue channels, which allows for a more accurate white to be displayed. - -QMK can automatically convert the RGB data to be sent to the LEDs to mix in the white channel: - -``` -w = min(r, g, b) -r -= w -g -= w -b -= w -``` - -Thus, an RGB triplet of `255,255,255` will simply turn on the white LED fully (`0,0,0,255`). - -To enable RGBW conversion, add the following to your `config.h`: - -```c -#define WS2812_RGBW -``` - -## Driver Configuration {#driver-configuration} - -Driver selection can be configured in `rules.mk` as `WS2812_DRIVER`, or in `info.json` as `ws2812.driver`. Valid values are `bitbang` (default), `i2c`, `spi`, `pwm`, `vendor`, or `custom`. See below for information on individual drivers. - -### Bitbang Driver {#bitbang-driver} - -This is the default WS2812 driver. It operates by "bit-banging" ie. directly toggling the GPIO. - -Please note that on AVR devices, due to the tight timing requirements longer chains and/or heavy CPU loads may cause visible lag. Unfortunately this driver is usually the only option for AVR. - -```make -WS2812_DRIVER = bitbang -``` - -### I2C Driver {#i2c-driver} - -A specialized driver mainly used for PS2AVRGB (Bootmapper Client) boards, which possess an ATtiny85 that handles the WS2812 LEDs. - -```make -WS2812_DRIVER = i2c -``` - -The following `#define`s apply only to the `i2c` driver: - -|Define |Default|Description | -|--------------------|-------|---------------------------------| -|`WS2812_I2C_ADDRESS`|`0xB0` |The I2C address of the ATtiny85. | -|`WS2812_I2C_TIMEOUT`|`100` |The I2C timeout, in milliseconds.| - -### PIO Driver {#pio-driver} - -This driver is RP2040-only, and leverages the onboard PIO (programmable I/O) system and DMA to offload processing from the CPU. - -The WS2812 PIO program uses one state machine, six instructions and one DMA interrupt handler callback. Due to the implementation the time resolution for this driver is 50 ns - any value not specified in this interval will be rounded to the next matching interval. - -```make -WS2812_DRIVER = vendor -``` - -### PWM Driver {#pwm-driver} - -This driver is ARM-only, and leverages the onboard PWM peripheral and DMA to offload processing from the CPU. - -```make -WS2812_DRIVER = pwm -``` - -### SPI Driver {#spi-driver} - -This driver is ARM-only, and leverages the onboard SPI peripheral and DMA to offload processing from the CPU. The DI pin **must** be connected to the MOSI pin on the MCU, and all other SPI pins **must** be left unused. This is also very dependent on your MCU's SPI peripheral clock speed, and may or may not be possible depending on the MCU selected. - -```make -WS2812_DRIVER = spi -``` - -## ChibiOS/ARM Configuration {#arm-configuration} - -The following defines apply only to ARM devices: - -|Define |Default |Description | -|------------|------------------------------|---------------------------------------------------------------------------------| -|`WS2812_T1L`|`(WS2812_TIMING - WS2812_T1H)`|The length of a "1" bit's low phase in nanoseconds (bitbang and PIO drivers only)| -|`WS2812_T0L`|`(WS2812_TIMING - WS2812_T0H)`|The length of a "0" bit's low phase in nanoseconds (bitbang and PIO drivers only)| - -### Push-Pull and Open Drain {#push-pull-open-drain} - -By default, the GPIO used for data transmission is configured as a *push-pull* output, meaning the pin is effectively always driven either to VCC or to ground. - -For situations where the logic level voltage is lower than the power supply voltage, however, this can pose an issue. The solution is to configure the pin for *open drain* mode instead, and use a pullup resistor between the DI pin and VCC. In this mode, the MCU can only pull the GPIO *low*, or leave it floating. The pullup resistor is then responsible for pulling the line high, when the MCU is not driving the GPIO. - -To configure the DI pin for open drain configuration, add the following to your `config.h`: - -```c -#define WS2812_EXTERNAL_PULLUP -``` - -### SPI Driver {#arm-spi-driver} - -Depending on the ChibiOS board configuration, you may need to enable SPI at the keyboard level. For STM32, this would look like: - -`halconf.h`: -```c -#define HAL_USE_SPI TRUE -``` -`mcuconf.h`: -```c -#undef STM32_SPI_USE_SPI1 -#define STM32_SPI_USE_SPI1 TRUE -``` - -The following `define`s apply only to the `spi` driver: - -|Define |Default |Description | -|--------------------------------|-------------|-------------------------------------------------------------------------------| -|`WS2812_SPI_DRIVER` |`SPID1` |The SPI driver to use | -|`WS2812_SPI_MOSI_PAL_MODE` |`5` |The MOSI pin alternative function to use | -|`WS2812_SPI_SCK_PIN` |*Not defined*|The SCK pin - required for F072 and possibly others | -|`WS2812_SPI_SCK_PAL_MODE` |`5` |The SCK pin alternative function to use - required for F072 and possibly others| -|`WS2812_SPI_DIVISOR` |`16` |The divisor used to adjust the baudrate | -|`WS2812_SPI_USE_CIRCULAR_BUFFER`|*Not defined*|Enable a circular buffer for improved rendering | - -#### Setting the Baudrate {#arm-spi-baudrate} - -To adjust the SPI baudrate, you will need to derive the target baudrate from the clock tree provided by STM32CubeMX, and add the following to your `config.h`: - -```c -#define WS2812_SPI_DIVISOR 16 -``` - -Only divisors of 2, 4, 8, 16, 32, 64, 128 and 256 are supported on STM32 devices. Other MCUs may have similar constraints -- check the reference manual for your respective MCU for specifics. - -#### Circular Buffer {#arm-spi-circular-buffer} - -A circular buffer can be enabled if you experience flickering. - -To enable the circular buffer, add the following to your `config.h`: - -```c -#define WS2812_SPI_USE_CIRCULAR_BUFFER -``` - -### PIO Driver {#arm-pio-driver} - -The following `#define`s apply only to the PIO driver: - -|Define |Default |Description | -|---------------------|-------------|---------------------------------------| -|`WS2812_PIO_USE_PIO1`|*Not defined*|Use the PIO1 peripheral instead of PIO0| - -### PWM Driver {#arm-pwm-driver} - -Depending on the ChibiOS board configuration, you may need to enable PWM at the keyboard level. For STM32, this would look like: - -`halconf.h`: -```c -#define HAL_USE_PWM TRUE -``` -`mcuconf.h`: -```c -#undef STM32_PWM_USE_TIM2 -#define STM32_PWM_USE_TIM2 TRUE -``` - -The following `#define`s apply only to the `pwm` driver: - -|Define |Default |Description | -|---------------------------------|--------------------|------------------------------------------------------------------------------------------| -|`WS2812_PWM_DRIVER` |`PWMD2` |The PWM driver to use | -|`WS2812_PWM_CHANNEL` |`2` |The PWM channel to use | -|`WS2812_PWM_PAL_MODE` |`2` |The pin alternative function to use | -|`WS2812_PWM_DMA_STREAM` |`STM32_DMA1_STREAM2`|The DMA Stream for `TIMx_UP` | -|`WS2812_PWM_DMA_CHANNEL` |`2` |The DMA Channel for `TIMx_UP` | -|`WS2812_PWM_DMAMUX_ID` |*Not defined* |The DMAMUX configuration for `TIMx_UP` - only required if your MCU has a DMAMUX peripheral| -|`WS2812_PWM_COMPLEMENTARY_OUTPUT`|*Not defined* |Whether the PWM output is complementary (`TIMx_CHyN`) | - -::: tip -Using a complementary timer output (`TIMx_CHyN`) is possible only for advanced-control timers (1, 8 and 20 on STM32), and the `STM32_PWM_USE_ADVANCED` option in `mcuconf.h` must be set to `TRUE`. Complementary outputs of general-purpose timers are not supported due to ChibiOS limitations. -::: - -## API {#api} - -### `void ws2812_setleds(rgb_led_t *ledarray, uint16_t number_of_leds)` {#api-ws2812-setleds} - -Send RGB data to the WS2812 LED chain. - -#### Arguments {#api-ws2812-setleds-arguments} - - - `rgb_led_t *ledarray` - A pointer to the LED array. - - `uint16_t number_of_leds` - The length of the LED array. -- cgit v1.2.3