commit
21c2324c3e
@ -0,0 +1,26 @@ |
||||
--- |
||||
BasedOnStyle: Google |
||||
AlignAfterOpenBracket: Align |
||||
AlignConsecutiveAssignments: 'true' |
||||
AlignConsecutiveDeclarations: 'true' |
||||
AlignOperands: 'true' |
||||
AllowAllParametersOfDeclarationOnNextLine: 'false' |
||||
AlwaysBreakAfterDefinitionReturnType: None |
||||
AlwaysBreakAfterReturnType: None |
||||
AlwaysBreakBeforeMultilineStrings: 'false' |
||||
BinPackArguments: 'true' |
||||
BinPackParameters: 'true' |
||||
ColumnLimit: '1000' |
||||
IndentCaseLabels: 'true' |
||||
IndentPPDirectives: AfterHash |
||||
IndentWidth: '4' |
||||
MaxEmptyLinesToKeep: '1' |
||||
PointerAlignment: Right |
||||
SortIncludes: 'false' |
||||
SpaceBeforeAssignmentOperators: 'true' |
||||
SpaceBeforeParens: ControlStatements |
||||
SpaceInEmptyParentheses: 'false' |
||||
TabWidth: '4' |
||||
UseTab: Never |
||||
|
||||
... |
@ -0,0 +1,5 @@ |
||||
--- |
||||
name: Blank issue |
||||
about: If you're 100% sure that you don't need one of the other issue templates, use this one instead. |
||||
|
||||
--- |
@ -0,0 +1,32 @@ |
||||
--- |
||||
name: Bug report |
||||
about: Create a report to help us improve the QMK Firmware |
||||
--- |
||||
<!-- Provide a general summary of the bug in the title above. --> |
||||
|
||||
<!--- This template is entirely optional and can be removed, but is here to help both you and us. --> |
||||
<!--- Anything on lines wrapped in comments like these will not show up in the final text. --> |
||||
|
||||
## Describe the Bug |
||||
|
||||
<!-- A clear and concise description of what the bug is. --> |
||||
|
||||
## System Information |
||||
|
||||
- Keyboard: |
||||
- Revision (if applicable): |
||||
- Operating system: |
||||
- AVR GCC version: |
||||
<!-- Run `avr-gcc --version` to find this out. --> |
||||
- ARM GCC version: |
||||
<!-- Run `arm-none-eabi-gcc --version` to find this out. --> |
||||
- QMK Firmware version: |
||||
<!-- Run `git describe --abbrev=0 --tags` to find this out. --> |
||||
- Any keyboard related software installed? |
||||
- [ ] AutoHotKey |
||||
- [ ] Karabiner |
||||
- [ ] Other: |
||||
|
||||
## Additional Context |
||||
|
||||
<!-- Add any other relevant information about the problem here. --> |
@ -0,0 +1,19 @@ |
||||
--- |
||||
name: Feature request |
||||
about: Suggest a new feature or changes to existing features |
||||
--- |
||||
<!--- Provide a general summary of the changes you want in the title above. --> |
||||
|
||||
<!--- This template is entirely optional and can be removed, but is here to help both you and us. --> |
||||
<!--- Anything on lines wrapped in comments like these will not show up in the final text. --> |
||||
|
||||
## Feature Request Type |
||||
|
||||
- [ ] Core functionality |
||||
- [ ] Add-on hardware support (eg. audio, RGB, OLED screen, etc.) |
||||
- [ ] Alteration (enhancement/optimization) of existing feature(s) |
||||
- [ ] New behavior |
||||
|
||||
## Description |
||||
|
||||
<!-- A few sentences describing what it is that you'd like to see in QMK. Additional information (such as links to spec sheets, licensing info, other related issues or PRs, etc) would be helpful. --> |
@ -0,0 +1,9 @@ |
||||
--- |
||||
name: Other issues |
||||
about: Anything else that doesn't fall into the above categories. |
||||
--- |
||||
<!--- Provide a general summary of the changes you want in the title above. --> |
||||
|
||||
<!--- Anything on lines wrapped in comments like these will not show up in the final text. --> |
||||
|
||||
<!-- Please check https://docs.qmk.fm/#/support for additional resources first. If that doesn't answer your question, choose the bug report template instead, as that may be more appropriate. --> |
@ -0,0 +1,34 @@ |
||||
<!--- Provide a general summary of your changes in the title above. --> |
||||
|
||||
<!--- This template is entirely optional and can be removed, but is here to help both you and us. --> |
||||
<!--- Anything on lines wrapped in comments like these will not show up in the final text. --> |
||||
|
||||
## Description |
||||
|
||||
<!--- Describe your changes in detail here. --> |
||||
|
||||
## Types of Changes |
||||
|
||||
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply. --> |
||||
- [ ] Core |
||||
- [ ] Bugfix |
||||
- [ ] New feature |
||||
- [ ] Enhancement/optimization |
||||
- [ ] Keyboard (addition or update) |
||||
- [ ] Keymap/layout/userspace (addition or update) |
||||
- [ ] Documentation |
||||
|
||||
## Issues Fixed or Closed by This PR |
||||
|
||||
* |
||||
|
||||
## Checklist |
||||
|
||||
<!--- Go over all the following points, and put an `x` in all the boxes that apply. --> |
||||
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> |
||||
- [ ] My code follows the code style of this project. |
||||
- [ ] My change requires a change to the documentation. |
||||
- [ ] I have updated the documentation accordingly. |
||||
- [ ] I have read the [**CONTRIBUTING** document](https://docs.qmk.fm/#/contributing). |
||||
- [ ] I have added tests to cover my changes. |
||||
- [ ] I have tested the changes and verified that they work and don't break anything (as well as I can manage). |
@ -1,28 +1,7 @@ |
||||
FROM debian:jessie |
||||
MAINTAINER Erik Dasque <erik@frenchguys.com> |
||||
FROM qmkfm/base_container |
||||
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y build-essential \ |
||||
gcc \ |
||||
unzip \ |
||||
wget \ |
||||
zip \ |
||||
gcc-avr \ |
||||
binutils-avr \ |
||||
avr-libc \ |
||||
dfu-programmer \ |
||||
dfu-util \ |
||||
gcc-arm-none-eabi \ |
||||
binutils-arm-none-eabi \ |
||||
libnewlib-arm-none-eabi \ |
||||
git \ |
||||
software-properties-common \ |
||||
avrdude \ |
||||
&& rm -rf /var/lib/apt/lists/* |
||||
VOLUME /qmk_firmware |
||||
WORKDIR /qmk_firmware |
||||
COPY . . |
||||
|
||||
ENV keyboard=ergodox |
||||
ENV subproject=ez |
||||
ENV keymap=default |
||||
|
||||
VOLUME /qmk |
||||
WORKDIR /qmk |
||||
CMD make clean ; make keyboard=${keyboard} subproject=${subproject} keymap=${keymap} |
||||
CMD make all:default |
||||
|
@ -0,0 +1,97 @@ |
||||
#!/usr/bin/env python3 |
||||
"""CLI wrapper for running QMK commands. |
||||
""" |
||||
import os |
||||
import subprocess |
||||
import sys |
||||
from glob import glob |
||||
from time import strftime |
||||
from importlib import import_module |
||||
from importlib.util import find_spec |
||||
|
||||
# Add the QMK python libs to our path |
||||
script_dir = os.path.dirname(os.path.realpath(__file__)) |
||||
qmk_dir = os.path.abspath(os.path.join(script_dir, '..')) |
||||
python_lib_dir = os.path.abspath(os.path.join(qmk_dir, 'lib', 'python')) |
||||
sys.path.append(python_lib_dir) |
||||
|
||||
# Change to the root of our checkout |
||||
os.environ['ORIG_CWD'] = os.getcwd() |
||||
os.chdir(qmk_dir) |
||||
|
||||
# Make sure our modules have been setup |
||||
with open('requirements.txt', 'r') as fd: |
||||
for line in fd.readlines(): |
||||
line = line.strip().replace('<', '=').replace('>', '=') |
||||
|
||||
if line[0] == '#': |
||||
continue |
||||
|
||||
if '#' in line: |
||||
line = line.split('#')[0] |
||||
|
||||
module = line.split('=')[0] if '=' in line else line |
||||
if not find_spec(module): |
||||
print('Your QMK build environment is not fully setup!\n') |
||||
print('Please run `./util/qmk_install.sh` to setup QMK.') |
||||
exit(255) |
||||
|
||||
# Figure out our version |
||||
command = ['git', 'describe', '--abbrev=6', '--dirty', '--always', '--tags'] |
||||
result = subprocess.run(command, text=True, capture_output=True) |
||||
|
||||
if result.returncode == 0: |
||||
os.environ['QMK_VERSION'] = 'QMK ' + result.stdout.strip() |
||||
else: |
||||
os.environ['QMK_VERSION'] = 'QMK ' + strftime('%Y-%m-%d-%H:%M:%S') |
||||
|
||||
# Setup the CLI |
||||
import milc |
||||
milc.EMOJI_LOGLEVELS['INFO'] = '{fg_blue}ψ{style_reset_all}' |
||||
|
||||
# If we were invoked as `qmk <cmd>` massage sys.argv into `qmk-<cmd>`. |
||||
# This means we can't accept arguments to the qmk script itself. |
||||
script_name = os.path.basename(sys.argv[0]) |
||||
if script_name == 'qmk': |
||||
if len(sys.argv) == 1: |
||||
milc.cli.log.error('No subcommand specified!\n') |
||||
|
||||
if len(sys.argv) == 1 or sys.argv[1] in ['-h', '--help']: |
||||
milc.cli.echo('usage: qmk <subcommand> [...]') |
||||
milc.cli.echo('\nsubcommands:') |
||||
subcommands = glob(os.path.join(qmk_dir, 'bin', 'qmk-*')) |
||||
for subcommand in sorted(subcommands): |
||||
subcommand = os.path.basename(subcommand).split('-', 1)[1] |
||||
milc.cli.echo('\t%s', subcommand) |
||||
milc.cli.echo('\nqmk <subcommand> --help for more information') |
||||
exit(1) |
||||
|
||||
if sys.argv[1] in ['-V', '--version']: |
||||
milc.cli.echo(os.environ['QMK_VERSION']) |
||||
exit(0) |
||||
|
||||
sys.argv[0] = script_name = '-'.join((script_name, sys.argv[1])) |
||||
del sys.argv[1] |
||||
|
||||
# Look for which module to import |
||||
if script_name == 'qmk': |
||||
milc.cli.print_help() |
||||
exit(0) |
||||
elif not script_name.startswith('qmk-'): |
||||
milc.cli.log.error('Invalid symlink, must start with "qmk-": %s', script_name) |
||||
else: |
||||
subcommand = script_name.replace('-', '.').replace('_', '.').split('.') |
||||
subcommand.insert(1, 'cli') |
||||
subcommand = '.'.join(subcommand) |
||||
|
||||
try: |
||||
import_module(subcommand) |
||||
except ModuleNotFoundError as e: |
||||
if e.__class__.__name__ != subcommand: |
||||
raise |
||||
|
||||
milc.cli.log.error('Invalid subcommand! Could not import %s.', subcommand) |
||||
exit(1) |
||||
|
||||
if __name__ == '__main__': |
||||
milc.cli() |
@ -0,0 +1 @@ |
||||
qmk |
@ -0,0 +1 @@ |
||||
qmk |
@ -0,0 +1 @@ |
||||
qmk |
@ -0,0 +1 @@ |
||||
qmk |
@ -0,0 +1,27 @@ |
||||
# Look for a json keymap file
|
||||
ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_5)/keymap.json)","") |
||||
KEYMAP_C := $(KEYBOARD_OUTPUT)/src/keymap.c
|
||||
KEYMAP_JSON := $(MAIN_KEYMAP_PATH_5)/keymap.json
|
||||
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_5)
|
||||
else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_4)/keymap.json)","") |
||||
KEYMAP_C := $(KEYBOARD_OUTPUT)/src/keymap.c
|
||||
KEYMAP_JSON := $(MAIN_KEYMAP_PATH_4)/keymap.json
|
||||
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_4)
|
||||
else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_3)/keymap.json)","") |
||||
KEYMAP_C := $(KEYBOARD_OUTPUT)/src/keymap.c
|
||||
KEYMAP_JSON := $(MAIN_KEYMAP_PATH_3)/keymap.json
|
||||
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_3)
|
||||
else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_2)/keymap.json)","") |
||||
KEYMAP_C := $(KEYBOARD_OUTPUT)/src/keymap.c
|
||||
KEYMAP_JSON := $(MAIN_KEYMAP_PATH_2)/keymap.json
|
||||
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_2)
|
||||
else ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_1)/keymap.json)","") |
||||
KEYMAP_C := $(KEYBOARD_OUTPUT)/src/keymap.c
|
||||
KEYMAP_JSON := $(MAIN_KEYMAP_PATH_1)/keymap.json
|
||||
KEYMAP_PATH := $(MAIN_KEYMAP_PATH_1)
|
||||
endif |
||||
|
||||
# Generate the keymap.c
|
||||
ifneq ("$(KEYMAP_JSON)","") |
||||
_ = $(shell test -e $(KEYMAP_C) || bin/qmk-json-keymap $(KEYMAP_JSON) -o $(KEYMAP_C))
|
||||
endif |
@ -0,0 +1,4 @@ |
||||
# Languages |
||||
|
||||
* [English](/) |
||||
* [Chinese](zh/) |
@ -1,97 +0,0 @@ |
||||
* [Complete Newbs Guide](newbs.md) |
||||
* [Getting Started](newbs_getting_started.md) |
||||
* [Building Your First Firmware](newbs_building_firmware.md) |
||||
* [Flashing Firmware](newbs_flashing.md) |
||||
* [Testing and Debugging](newbs_testing_debugging.md) |
||||
* [Best Practices](newbs_best_practices.md) |
||||
* [Learning Resources](newbs_learn_more_resources.md) |
||||
|
||||
* [QMK Basics](README.md) |
||||
* [QMK Introduction](getting_started_introduction.md) |
||||
* [Contributing to QMK](contributing.md) |
||||
* [How to Use Github](getting_started_github.md) |
||||
* [Getting Help](getting_started_getting_help.md) |
||||
|
||||
* [FAQ](faq.md) |
||||
* [General FAQ](faq_general.md) |
||||
* [Build/Compile QMK](faq_build.md) |
||||
* [Debugging/Troubleshooting QMK](faq_debug.md) |
||||
* [Keymap](faq_keymap.md) |
||||
|
||||
* Detailed Guides |
||||
* [Install Build Tools](getting_started_build_tools.md) |
||||
* [Vagrant Guide](getting_started_vagrant.md) |
||||
* [Build/Compile Instructions](getting_started_make_guide.md) |
||||
* [Flashing Firmware](flashing.md) |
||||
* [Customizing Functionality](custom_quantum_functions.md) |
||||
* [Keymap Overview](keymap.md) |
||||
|
||||
* [Hardware](hardware.md) |
||||
* [AVR Processors](hardware_avr.md) |
||||
* [Drivers](hardware_drivers.md) |
||||
|
||||
* Reference |
||||
* [Keyboard Guidelines](hardware_keyboard_guidelines.md) |
||||
* [Config Options](config_options.md) |
||||
* [Keycodes](keycodes.md) |
||||
* [Documentation Best Practices](documentation_best_practices.md) |
||||
* [Documentation Templates](documentation_templates.md) |
||||
* [Glossary](reference_glossary.md) |
||||
* [Unit Testing](unit_testing.md) |
||||
* [Useful Functions](ref_functions.md) |
||||
|
||||
* [Features](features.md) |
||||
* [Basic Keycodes](keycodes_basic.md) |
||||
* [Quantum Keycodes](quantum_keycodes.md) |
||||
* [Advanced Keycodes](feature_advanced_keycodes.md) |
||||
* [Audio](feature_audio.md) |
||||
* [Auto Shift](feature_auto_shift.md) |
||||
* [Backlight](feature_backlight.md) |
||||
* [Bluetooth](feature_bluetooth.md) |
||||
* [Bootmagic](feature_bootmagic.md) |
||||
* [Combos](feature_combo) |
||||
* [Command](feature_command.md) |
||||
* [Dynamic Macros](feature_dynamic_macros.md) |
||||
* [Encoders](feature_encoders.md) |
||||
* [Grave Escape](feature_grave_esc.md) |
||||
* [Key Lock](feature_key_lock.md) |
||||
* [Layouts](feature_layouts.md) |
||||
* [Leader Key](feature_leader_key.md) |
||||
* [Macros](feature_macros.md) |
||||
* [Mouse Keys](feature_mouse_keys.md) |
||||
* [One Shot Keys](feature_advanced_keycodes.md#one-shot-keys) |
||||
* [Pointing Device](feature_pointing_device.md) |
||||
* [PS/2 Mouse](feature_ps2_mouse.md) |
||||
* [RGB Lighting](feature_rgblight.md) |
||||
* [RGB Matrix](feature_rgb_matrix.md) |
||||
* [Space Cadet Shift](feature_space_cadet_shift.md) |
||||
* [Space Cadet Shift Enter](feature_space_cadet_shift_enter.md) |
||||
* [Stenography](feature_stenography.md) |
||||
* [Swap Hands](feature_swap_hands.md) |
||||
* [Tap Dance](feature_tap_dance.md) |
||||
* [Terminal](feature_terminal.md) |
||||
* [Thermal Printer](feature_thermal_printer.md) |
||||
* [Unicode](feature_unicode.md) |
||||
* [Userspace](feature_userspace.md) |
||||
* [US ANSI Shifted Keys](keycodes_us_ansi_shifted.md) |
||||
|
||||
* For Makers and Modders |
||||
* [Hand Wiring Guide](hand_wire.md) |
||||
* [ISP Flashing Guide](isp_flashing_guide.md) |
||||
* [ARM Debugging Guide](arm_debugging.md) |
||||
|
||||
* For a Deeper Understanding |
||||
* [How Keyboards Work](how_keyboards_work.md) |
||||
* [Understanding QMK](understanding_qmk.md) |
||||
|
||||
* Other Topics |
||||
* [Using Eclipse with QMK](eclipse.md) |
||||
|
||||
* QMK Internals (In Progress) |
||||
* [Defines](internals_defines.md) |
||||
* [Input Callback Reg](internals_input_callback_reg.md) |
||||
* [Midi Device](internals_midi_device.md) |
||||
* [Midi Device Setup Process](internals_midi_device_setup_process.md) |
||||
* [Midi Util](internals_midi_util.md) |
||||
* [Send Functions](internals_send_functions.md) |
||||
* [Sysex Tools](internals_sysex_tools.md) |
@ -0,0 +1,31 @@ |
||||
# QMK CLI |
||||
|
||||
This page describes how to setup and use the QMK CLI. |
||||
|
||||
# Overview |
||||
|
||||
The QMK CLI makes building and working with QMK keyboards easier. We have provided a number of commands to help you work with QMK: |
||||
|
||||
* `qmk compile-json` |
||||
|
||||
# Setup |
||||
|
||||
Simply add the `qmk_firmware/bin` directory to your `PATH`. You can run the `qmk` commands from any directory. |
||||
|
||||
``` |
||||
export PATH=$PATH:$HOME/qmk_firmware/bin |
||||
``` |
||||
|
||||
You may want to add this to your `.profile`, `.bash_profile`, `.zsh_profile`, or other shell startup scripts. |
||||
|
||||
# Commands |
||||
|
||||
## `qmk compile-json` |
||||
|
||||
This command allows you to compile JSON files you have downloaded from <https://config.qmk.fm>. |
||||
|
||||
**Usage**: |
||||
|
||||
``` |
||||
qmk compile-json mine.json |
||||
``` |
@ -0,0 +1,58 @@ |
||||
# Coding Conventions (C) |
||||
|
||||
Most of our style is pretty easy to pick up on, but right now it's not entirely consistent. You should match the style of the code surrounding your change, but if that code is inconsistent or unclear use the following guidelines: |
||||
|
||||
* We indent using four (4) spaces (soft tabs) |
||||
* We use a modified One True Brace Style |
||||
* Opening Brace: At the end of the same line as the statement that opens the block |
||||
* Closing Brace: Lined up with the first character of the statement that opens the block |
||||
* Else If: Place the closing brace at the beginning of the line and the next opening brace at the end of the same line. |
||||
* Optional Braces: Always include optional braces. |
||||
* Good: if (condition) { return false; } |
||||
* Bad: if (condition) return false; |
||||
* We encourage use of C style comments: `/* */` |
||||
* Think of them as a story describing the feature |
||||
* Use them liberally to explain why particular decisions were made. |
||||
* Do not write obvious comments |
||||
* If you not sure if a comment is obvious, go ahead and include it. |
||||
* In general we don't wrap lines, they can be as long as needed. If you do choose to wrap lines please do not wrap any wider than 76 columns. |
||||
* We use `#pragma once` at the start of header files rather than old-style include guards (`#ifndef THIS_FILE_H`, `#define THIS_FILE_H`, ..., `#endif`) |
||||
* We accept both forms of preprocessor if's: `#ifdef DEFINED` and `#if defined(DEFINED)` |
||||
* If you are not sure which to prefer use the `#if defined(DEFINED)` form. |
||||
* Do not change existing code from one style to the other, except when moving to a multiple condition `#if`. |
||||
* Do not put whitespace between `#` and `if`. |
||||
* When deciding how (or if) to indent directives keep these points in mind: |
||||
* Readability is more important than consistency. |
||||
* Follow the file's existing style. If the file is mixed follow the style that makes sense for the section you are modifying. |
||||
* When choosing to indent you can follow the indention level of the surrounding C code, or preprocessor directives can have their own indent level. Choose the style that best communicates the intent of your code. |
||||
|
||||
Here is an example for easy reference: |
||||
|
||||
```c |
||||
/* Enums for foo */ |
||||
enum foo_state { |
||||
FOO_BAR, |
||||
FOO_BAZ, |
||||
}; |
||||
|
||||
/* Returns a value */ |
||||
int foo(void) { |
||||
if (some_condition) { |
||||
return FOO_BAR; |
||||
} else { |
||||
return -1; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
# Auto-formatting with clang-format |
||||
|
||||
[Clang-format](https://clang.llvm.org/docs/ClangFormat.html) is part of LLVM and can automatically format your code for you, because ain't nobody got time to do it manually. We supply a configuration file for it that applies most of the coding conventions listed above. It will only change whitespace and newlines, so you will still have to remember to include optional braces yourself. |
||||
|
||||
Use the [full LLVM installer](http://llvm.org/builds/) to get clang-format on Windows, or use `sudo apt install clang-format` on Ubuntu. |
||||
|
||||
If you run it from the command-line, pass `-style=file` as an option and it will automatically find the .clang-format configuration file in the QMK root directory. |
||||
|
||||
If you use VSCode, the standard C/C++ plugin supports clang-format, alternatively there is a [separate extension](https://marketplace.visualstudio.com/items?itemName=LLVMExtensions.ClangFormat) for it. |
||||
|
||||
Some things (like LAYOUT macros) are destroyed by clang-format, so either don't run it on those files, or wrap the sensitive code in `// clang-format off` and `// clang-format on`. |
@ -0,0 +1,314 @@ |
||||
# Coding Conventions (Python) |
||||
|
||||
Most of our style follows PEP8 with some local modifications to make things less nit-picky. |
||||
|
||||
* We target Python 3.5 for compatability with all supported platforms. |
||||
* We indent using four (4) spaces (soft tabs) |
||||
* We encourage liberal use of comments |
||||
* Think of them as a story describing the feature |
||||
* Use them liberally to explain why particular decisions were made. |
||||
* Do not write obvious comments |
||||
* If you not sure if a comment is obvious, go ahead and include it. |
||||
* We require useful docstrings for all functions. |
||||
* In general we don't wrap lines, they can be as long as needed. If you do choose to wrap lines please do not wrap any wider than 76 columns. |
||||
* Some of our practices conflict with the wider python community to make our codebase more approachable to non-pythonistas. |
||||
|
||||
# YAPF |
||||
|
||||
You can use [yapf](https://github.com/google/yapf) to style your code. We provide a config in [setup.cfg](setup.cfg). |
||||
|
||||
# Imports |
||||
|
||||
We don't have a hard and fast rule for when to use `import ...` vs `from ... import ...`. Understandability and maintainability is our ultimate goal. |
||||
|
||||
Generally we prefer to import specific function and class names from a module to keep code shorter and easier to understand. Sometimes this results in a name that is ambiguous, and in such cases we prefer to import the module instead. You should avoid using the "as" keyword when importing, unless you are importing a compatability module. |
||||
|
||||
Imports should be one line per module. We group import statements together using the standard python rules- system, 3rd party, local. |
||||
|
||||
Do not use `from foo import *`. Supply a list of objects you want to import instead, or import the whole module. |
||||
|
||||
## Import Examples |
||||
|
||||
Good: |
||||
|
||||
``` |
||||
from qmk import effects |
||||
|
||||
effects.echo() |
||||
``` |
||||
|
||||
Bad: |
||||
|
||||
``` |
||||
from qmk.effects import echo |
||||
|
||||
echo() # It's unclear where echo comes from |
||||
``` |
||||
|
||||
Good: |
||||
|
||||
``` |
||||
from qmk.keymap import compile_firmware |
||||
|
||||
compile_firmware() |
||||
``` |
||||
|
||||
OK, but the above is better: |
||||
|
||||
``` |
||||
import qmk.keymap |
||||
|
||||
qmk.keymap.compile_firmware() |
||||
``` |
||||
|
||||
# Statements |
||||
|
||||
One statement per line. |
||||
|
||||
Even when allowed (EG `if foo: bar`) we do not combine 2 statements onto a single line. |
||||
|
||||
# Naming |
||||
|
||||
`module_name`, `package_name`, `ClassName`, `method_name`, `ExceptionName`, `function_name`, `GLOBAL_CONSTANT_NAME`, `global_var_name`, `instance_var_name`, `function_parameter_name`, `local_var_name`. |
||||
|
||||
Function names, variable names, and filenames should be descriptive; eschew abbreviation. In particular, do not use abbreviations that are ambiguous or unfamiliar to readers outside your project, and do not abbreviate by deleting letters within a word. |
||||
|
||||
Always use a .py filename extension. Never use dashes. |
||||
|
||||
## Names to Avoid |
||||
|
||||
* single character names except for counters or iterators. You may use "e" as an exception identifier in try/except statements. |
||||
* dashes (-) in any package/module name |
||||
* __double_leading_and_trailing_underscore__ names (reserved by Python) |
||||
|
||||
# Docstrings |
||||
|
||||
To maintain consistency with our docstrings we've set out the following guidelines. |
||||
|
||||
* Use markdown formatting |
||||
* Always use triple-dquote docstrings with at least one linebreak: `"""\n"""` |
||||
* First line is a short (< 70 char) description of what the function does |
||||
* If you need more in your docstring leave a blank line between the description and the rest. |
||||
* Start indented lines at the same indent level as the opening triple-dquote |
||||
* Document all function arguments using the format described below |
||||
* If present, Args:, Returns:, and Raises: should be the last three things in the docstring, separated by a blank line each. |
||||
|
||||
## Simple docstring example |
||||
|
||||
``` |
||||
def my_awesome_function(): |
||||
"""Return the number of seconds since 1970 Jan 1 00:00 UTC. |
||||
""" |
||||
return int(time.time()) |
||||
``` |
||||
|
||||
## Complex docstring example |
||||
|
||||
``` |
||||
def my_awesome_function(): |
||||
"""Return the number of seconds since 1970 Jan 1 00:00 UTC. |
||||
|
||||
This function always returns an integer number of seconds. |
||||
""" |
||||
return int(time.time()) |
||||
``` |
||||
|
||||
## Function arguments docstring example |
||||
|
||||
``` |
||||
def my_awesome_function(start=None, offset=0): |
||||
"""Return the number of seconds since 1970 Jan 1 00:00 UTC. |
||||
|
||||
This function always returns an integer number of seconds. |
||||
|
||||
|
||||
Args: |
||||
start |
||||
The time to start at instead of 1970 Jan 1 00:00 UTC |
||||
|
||||
offset |
||||
Return an answer that has this number of seconds subtracted first |
||||
|
||||
Returns: |
||||
An integer describing a number of seconds. |
||||
|
||||
Raises: |
||||
ValueError |
||||
When `start` or `offset` are not positive numbers |
||||
""" |
||||
if start < 0 or offset < 0: |
||||
raise ValueError('start and offset must be positive numbers.') |
||||
|
||||
if not start: |
||||
start = time.time() |
||||
|
||||
return int(start - offset) |
||||
``` |
||||
|
||||
# Exceptions |
||||
|
||||
Exceptions are used to handle exceptional situations. They should not be used for flow control. This is a break from the python norm of "ask for forgiveness." If you are catching an exception it should be to handle a situation that is unusual. |
||||
|
||||
If you use a catch-all exception for any reason you must log the exception and stacktrace using cli.log. |
||||
|
||||
Make your try/except blocks as short as possible. If you need a lot of try statements you may need to restructure your code. |
||||
|
||||
# Tuples |
||||
|
||||
When defining one-item tuples always include a trailing comma so that it is obvious you are using a tuple. Do not rely on implicit one-item tuple unpacking. Better still use a list which is unambiguous. |
||||
|
||||
This is particularly important when using the printf-style format strings that are commonly used. |
||||
|
||||
# Lists and Dictionaries |
||||
|
||||
We have configured YAPF to differentiate between sequence styles with a trailing comma. When a trailing comma is omitted YAPF will format the sequence as a single line. When a trailing comma is included YAPF will format the sequence with one item per line. |
||||
|
||||
You should generally prefer to keep short definition on a single line. Break out to multiple lines sooner rather than later to aid readability and maintainability. |
||||
|
||||
# Parentheses |
||||
|
||||
Avoid excessive parentheses, but do use parentheses to make code easier to understand. Do not use them in return statements unless you are explicitly returning a tuple, or it is part of a math expression. |
||||
|
||||
# Format Strings |
||||
|
||||
We generally prefer printf-style format strings. Example: |
||||
|
||||
``` |
||||
name = 'World' |
||||
print('Hello, %s!' % (name,)) |
||||
``` |
||||
|
||||
This style is used by the logging module, which we make use of extensively, and we have adopted it in other places for consistency. It is also more familiar to C programmers, who are a big part of our casual audience. |
||||
|
||||
Our included CLI module has support for using these without using the percent (%) operator. Look at `cli.echo()` and the various `cli.log` functions (EG, `cli.log.info()`) for more details. |
||||
|
||||
# Comprehensions & Generator Expressions |
||||
|
||||
We encourage the liberal use of comprehensions and generators, but do not let them get too complex. If you need complexity fall back to a for loop that is easier to understand. |
||||
|
||||
# Lambdas |
||||
|
||||
OK to use but probably should be avoided. With comprehensions and generators the need for lambdas is not as strong as it once was. |
||||
|
||||
# Conditional Expressions |
||||
|
||||
OK in variable assignment, but otherwise should be avoided. |
||||
|
||||
Conditional expressions are if statements that are in line with code. For example: |
||||
|
||||
``` |
||||
x = 1 if cond else 2 |
||||
``` |
||||
|
||||
It's generally not a good idea to use these as function arguments, sequence items, etc. It's too easy to overlook. |
||||
|
||||
# Default Argument Values |
||||
|
||||
Encouraged, but values must be immutable objects. |
||||
|
||||
When specifying default values in argument lists always be careful to specify objects that can't be modified in place. If you use a mutable object the changes you make will persist between calls, which is usually not what you want. Even if that is what you intend to do it is confusing for others and will hinder understanding. |
||||
|
||||
Bad: |
||||
|
||||
``` |
||||
def my_func(foo={}): |
||||
pass |
||||
``` |
||||
|
||||
Good: |
||||
|
||||
``` |
||||
def my_func(foo=None): |
||||
if not foo: |
||||
foo = {} |
||||
``` |
||||
|
||||
# Properties |
||||
|
||||
Always use properties instead of getter and setter functions. |
||||
|
||||
``` |
||||
class Foo(object): |
||||
def __init__(self): |
||||
self._bar = None |
||||
|
||||
@property |
||||
def bar(self): |
||||
return self._bar |
||||
|
||||
@bar.setter |
||||
def bar(self, bar): |
||||
self._bar = bar |
||||
``` |
||||
|
||||
# True/False Evaluations |
||||
|
||||
You should generally prefer the implicit True/False evaluation in if statements, rather than checking equivalency. |
||||
|
||||
Bad: |
||||
|
||||
``` |
||||
if foo == True: |
||||
pass |
||||
|
||||
if bar == False: |
||||
pass |
||||
``` |
||||
|
||||
Good: |
||||
|
||||
``` |
||||
if foo: |
||||
pass |
||||
|
||||
if not bar: |
||||
pass |
||||
``` |
||||
|
||||
# Decorators |
||||
|
||||
Use when appropriate. Try to avoid too much magic unless it helps with understanding. |
||||
|
||||
# Threading and Multiprocessing |
||||
|
||||
Should be avoided. If you need this you will have to make a strong case before we merge your code. |
||||
|
||||
# Power Features |
||||
|
||||
Python is an extremely flexible language and gives you many fancy features such as custom metaclasses, access to bytecode, on-the-fly compilation, dynamic inheritance, object reparenting, import hacks, reflection, modification of system internals, etc. |
||||
|
||||
Don't use these. |
||||
|
||||
Performance is not a critical concern for us, and code understandability is. We want our codebase to be approachable by someone who only has a day or two to play with it. These features generally come with a cost to easy understanding, and we would prefer to have code that can be readily understood over faster or more compact code. |
||||
|
||||
Note that some standard library modules use these techniques and it is ok to make use of those modules. But please keep readability and understandability in mind when using them. |
||||
|
||||
# Type Annotated Code |
||||
|
||||
For now we are not using any type annotation system, and would prefer that code remain unannotated. We may revisit this in the future. |
||||
|
||||
# Function length |
||||
|
||||
Prefer small and focused functions. |
||||
|
||||
We recognize that long functions are sometimes appropriate, so no hard limit is placed on function length. If a function exceeds about 40 lines, think about whether it can be broken up without harming the structure of the program. |
||||
|
||||
Even if your long function works perfectly now, someone modifying it in a few months may add new behavior. This could result in bugs that are hard to find. Keeping your functions short and simple makes it easier for other people to read and modify your code. |
||||
|
||||
You could find long and complicated functions when working with some code. Do not be intimidated by modifying existing code: if working with such a function proves to be difficult, you find that errors are hard to debug, or you want to use a piece of it in several different contexts, consider breaking up the function into smaller and more manageable pieces. |
||||
|
||||
# FIXMEs |
||||
|
||||
It is OK to leave FIXMEs in code. Why? Encouraging people to at least document parts of code that need to be thought out more (or that are confusing) is better than leaving this code undocumented. |
||||
|
||||
All FIXMEs should be formatted like: |
||||
|
||||
``` |
||||
FIXME(username): Revisit this code when the frob feature is done. |
||||
``` |
||||
|
||||
...where username is your GitHub username. |
||||
|
||||
# Unit Tests |
||||
|
||||
These are good. We should have some one day. |
@ -0,0 +1,42 @@ |
||||
# Debounce algorithm |
||||
|
||||
QMK supports multiple debounce algorithms through its debounce API. |
||||
|
||||
The logic for which debounce method called is below. It checks various defines that you have set in rules.mk |
||||
|
||||
``` |
||||
DEBOUNCE_DIR:= $(QUANTUM_DIR)/debounce |
||||
DEBOUNCE_TYPE?= sym_g |
||||
ifneq ($(strip $(DEBOUNCE_TYPE)), custom) |
||||
QUANTUM_SRC += $(DEBOUNCE_DIR)/$(strip $(DEBOUNCE_TYPE)).c |
||||
endif |
||||
``` |
||||
|
||||
# Debounce selection |
||||
|
||||
| DEBOUNCE_TYPE | Description | What else is needed | |
||||
| ------------- | --------------------------------------------------- | ----------------------------- | |
||||
| Not defined | Use the default algorithm, currently sym_g | Nothing | |
||||
| custom | Use your own debounce.c | ```SRC += debounce.c``` add your own debounce.c and implement necessary functions | |
||||
| anything_else | Use another algorithm from quantum/debounce/* | Nothing | |
||||
|
||||
**Regarding split keyboards**: |
||||
The debounce code is compatible with split keyboards. |
||||
|
||||
# Use your own debouncing code |
||||
* Set ```DEBOUNCE_TYPE = custom ```. |
||||
* Add ```SRC += debounce.c``` |
||||
* Add your own ```debounce.c```. Look at current implementations in ```quantum/debounce``` for examples. |
||||
* Debouncing occurs after every raw matrix scan. |
||||
* Use num_rows rather than MATRIX_ROWS, so that split keyboards are supported correctly. |
||||
|
||||
# Changing between included debouncing methods |
||||
You can either use your own code, by including your own debounce.c, or switch to another included one. |
||||
Included debounce methods are: |
||||
* eager_pr - debouncing per row. On any state change, response is immediate, followed by locking the row ```DEBOUNCE_DELAY``` milliseconds of no further input for that row. |
||||
For use in keyboards where refreshing ```NUM_KEYS``` 8-bit counters is computationally expensive / low scan rate, and fingers usually only hit one row at a time. This could be |
||||
appropriate for the ErgoDox models; the matrix is rotated 90°, and hence its "rows" are really columns, and each finger only hits a single "row" at a time in normal use. |
||||
* eager_pk - debouncing per key. On any state change, response is immediate, followed by ```DEBOUNCE_DELAY``` milliseconds of no further input for that key |
||||
* sym_g - debouncing per keyboard. On any state change, a global timer is set. When ```DEBOUNCE_DELAY``` milliseconds of no changes has occured, all input changes are pushed. |
||||
|
||||
|
@ -0,0 +1,147 @@ |
||||
# Haptic Feedback |
||||
|
||||
## Haptic feedback rules.mk options |
||||
|
||||
The following options are currently available for haptic feedback in `rule.mk`: |
||||
|
||||
`HAPTIC_ENABLE += DRV2605L` |
||||
|
||||
`HAPTIC_ENABLE += SOLENOID` |
||||
|
||||
## 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. |
||||
|
||||
| Name | Description | |
||||
|-----------|-------------------------------------------------------| |
||||
|`HPT_ON` | Turn haptic feedback on | |
||||
|`HPT_OFF` | Turn haptic feedback on | |
||||
|`HPT_TOG` | Toggle haptic feedback on/off | |
||||
|`HPT_RST` | Reset haptic feedback config to default | |
||||
|`HPT_FBK` | Toggle feedback to occur on keypress, release or both | |
||||
|`HPT_BUZ` | Toggle solenoid buzz on/off | |
||||
|`HPT_MODI` | Go to next DRV2605L waveform | |
||||
|`HPT_MODD` | Go to previous DRV2605L waveform | |
||||
|`HPT_DWLI` | Increase Solenoid dwell time | |
||||
|`HPT_DWLD` | Decrease Solenoid dwell time | |
||||
|
||||
### Solenoids |
||||
|
||||
First 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://playground.arduino.cc/uploads/Learning/solenoid_driver.pdf) |
||||
|
||||
Select a pin that has PWM for the signal pin |
||||
|
||||
``` |
||||
#define SOLENOID_PIN *pin* |
||||
``` |
||||
|
||||
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. |
||||
|
||||
``` |
||||
#define FB_ERM_LRA 0 |
||||
#define FB_BRAKEFACTOR 3 /* For 1x:0, 2x:1, 3x:2, 4x:3, 6x:4, 8x:5, 16x:6, Disable Braking:7 */ |
||||
#define 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 RATED_VOLTAGE 3 |
||||
#define 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. |
||||
|
||||
``` |
||||
#define FB_ERM_LRA 1 |
||||
#define FB_BRAKEFACTOR 3 /* For 1x:0, 2x:1, 3x:2, 4x:3, 6x:4, 8x:5, 16x:6, Disable Braking:7 */ |
||||
#define 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 RATED_VOLTAGE 2 |
||||
#define V_PEAK 2.8 |
||||
#define V_RMS 2.0 |
||||
#define V_PEAK 2.1 |
||||
#define 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 |
||||
|
||||
``` |
||||
#define DRV_GREETING *sequence name or number* |
||||
``` |
||||
If haptic feedback is enabled, the keyboard will vibrate to a specific sqeuence during startup. That can be selected using the following define: |
||||
|
||||
``` |
||||
#define DRV_MODE_DEFAULT *sequence name or number* |
||||
``` |
||||
This will set what sequence HPT_RST will set as the active mode. If not defined, mode will be set to 1 when HPT_RST is pressed. |
@ -0,0 +1,90 @@ |
||||
# 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.md) instead. |
||||
|
||||
## Driver configuration |
||||
|
||||
### IS31FL3731 |
||||
|
||||
There is basic support for addressable LED matrix lighting with the I2C IS31FL3731 RGB controller. To enable it, add this to your `rules.mk`: |
||||
|
||||
LED_MATRIX_ENABLE = IS31FL3731 |
||||
|
||||
You can use between 1 and 4 IS31FL3731 IC's. Do not specify `LED_DRIVER_ADDR_<N>` defines for IC's that are not present on your keyboard. You can define the following items in `config.h`: |
||||
|
||||
| Variable | Description | Default | |
||||
|----------|-------------|---------| |
||||
| `ISSI_TIMEOUT` | (Optional) How long to wait for i2c messages | 100 | |
||||
| `ISSI_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 | |
||||
| `LED_DRIVER_COUNT` | (Required) How many LED driver IC's are present | | |
||||
| `LED_DRIVER_LED_COUNT` | (Required) How many LED lights are present across all drivers | | |
||||
| `LED_DRIVER_ADDR_1` | (Required) Address for the first LED driver | | |
||||
| `LED_DRIVER_ADDR_2` | (Optional) Address for the second LED driver | | |
||||
| `LED_DRIVER_ADDR_3` | (Optional) Address for the third LED driver | | |
||||
| `LED_DRIVER_ADDR_4` | (Optional) Address for the fourth LED driver | | |
||||
|
||||
Here is an example using 2 drivers. |
||||
|
||||
// This is a 7-bit address, that gets left-shifted and bit 0 |
||||
// set to 0 for write, 1 for read (as per I2C protocol) |
||||
// The address will vary depending on your wiring: |
||||
// 0b1110100 AD <-> GND |
||||
// 0b1110111 AD <-> VCC |
||||
// 0b1110101 AD <-> SCL |
||||
// 0b1110110 AD <-> SDA |
||||
#define LED_DRIVER_ADDR_1 0b1110100 |
||||
#define LED_DRIVER_ADDR_2 0b1110110 |
||||
|
||||
#define LED_DRIVER_COUNT 2 |
||||
#define LED_DRIVER_1_LED_COUNT 25 |
||||
#define LED_DRIVER_2_LED_COUNT 24 |
||||
#define LED_DRIVER_LED_COUNT LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL |
||||
|
||||
Currently only 2 drivers are supported, but it would be trivial to support all 4 combinations. |
||||
|
||||
Define these arrays listing all the LEDs in your `<keyboard>.c`: |
||||
|
||||
const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { |
||||
/* Refer to IS31 manual for these locations |
||||
* driver |
||||
* | LED address |
||||
* | | */ |
||||
{0, C3_3}, |
||||
.... |
||||
} |
||||
|
||||
Where `Cx_y` is the location of the LED in the matrix defined by [the datasheet](http://www.issi.com/WW/pdf/31FL3731.pdf) and the header file `drivers/issi/is31fl3731-simple.h`. The `driver` is the index of the driver you defined in your `config.h` (`0`, `1`, `2`, or `3` ). |
||||
|
||||
## Keycodes |
||||
|
||||
All LED matrix keycodes are currently shared with the [backlight system](feature_backlight.md). |
||||
|
||||
## LED Matrix Effects |
||||
|
||||
Currently no LED matrix effects have been created. |
||||
|
||||
## Custom layer effects |
||||
|
||||
Custom layer effects can be done by defining this in your `<keyboard>.c`: |
||||
|
||||
void led_matrix_indicators_kb(void) { |
||||
led_matrix_set_index_value(index, value); |
||||
} |
||||
|
||||
A similar function works in the keymap as `led_matrix_indicators_user`. |
||||
|
||||
## Suspended state |
||||
|
||||
To use the suspend feature, add this to your `<keyboard>.c`: |
||||
|
||||
void suspend_power_down_kb(void) |
||||
{ |
||||
led_matrix_set_suspend_state(true); |
||||
} |
||||
|
||||
void suspend_wakeup_init_kb(void) |
||||
{ |
||||
led_matrix_set_suspend_state(false); |
||||
} |
@ -1,81 +1,119 @@ |
||||
# Mousekeys |
||||
# 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. |
||||
|
||||
Mousekeys is a feature that allows you to emulate a mouse using your keyboard. You can move the pointer around, click up to 5 buttons, and even scroll in all 4 directions. QMK 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). |
||||
## Adding mouse keys to your keyboard |
||||
|
||||
## Adding Mousekeys to a Keymap |
||||
To use mouse keys, you must at least enable mouse keys support and map mouse actions to keys on your keyboard. |
||||
|
||||
There are two steps to adding Mousekeys support to your keyboard. You must enable support in the `rules.mk` file and you must map mouse actions to keys on your keyboard. |
||||
### Enabling mouse keys |
||||
|
||||
### Adding Mousekeys Support in the `rules.mk` |
||||
To enable mouse keys, add the following line to your keymap’s `rules.mk`: |
||||
|
||||
To add support for Mousekeys you simply need to add a single line to your keymap's `rules.mk`: |
||||
|
||||
``` |
||||
```c |
||||
MOUSEKEY_ENABLE = yes |
||||
``` |
||||
|
||||
You can see an example here: https://github.com/qmk/qmk_firmware/blob/master/keyboards/clueboard/66/keymaps/mouse_keys/rules.mk |
||||
### Mapping mouse actions |
||||
|
||||
### Mapping Mouse Actions to Keyboard Keys |
||||
|
||||
You can use these keycodes within your keymap to map button presses to 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`|Mouse Cursor Up | |
||||
|`KC_MS_DOWN` |`KC_MS_D`|Mouse Cursor Down | |
||||
|`KC_MS_LEFT` |`KC_MS_L`|Mouse Cursor Left | |
||||
|`KC_MS_RIGHT` |`KC_MS_R`|Mouse Cursor Right | |
||||
|`KC_MS_BTN1` |`KC_BTN1`|Mouse Button 1 | |
||||
|`KC_MS_BTN2` |`KC_BTN2`|Mouse Button 2 | |
||||
|`KC_MS_BTN3` |`KC_BTN3`|Mouse Button 3 | |
||||
|`KC_MS_BTN4` |`KC_BTN4`|Mouse Button 4 | |
||||
|`KC_MS_BTN5` |`KC_BTN5`|Mouse Button 5 | |
||||
|`KC_MS_WH_UP` |`KC_WH_U`|Mouse Wheel Up | |
||||
|`KC_MS_WH_DOWN` |`KC_WH_D`|Mouse Wheel Down | |
||||
|`KC_MS_WH_LEFT` |`KC_WH_L`|Mouse Wheel Left | |
||||
|`KC_MS_WH_RIGHT`|`KC_WH_R`|Mouse Wheel Right | |
||||
|`KC_MS_ACCEL0` |`KC_ACL0`|Set mouse acceleration to 0| |
||||
|`KC_MS_ACCEL1` |`KC_ACL1`|Set mouse acceleration to 1| |
||||
|`KC_MS_ACCEL2` |`KC_ACL2`|Set mouse acceleration to 2| |
||||
|
||||
You can see an example in the `_ML` here: https://github.com/qmk/qmk_firmware/blob/master/keyboards/clueboard/66/keymaps/mouse_keys/keymap.c#L46 |
||||
|
||||
## Configuring the Behavior of Mousekeys |
||||
|
||||
The default speed for controlling the mouse with the keyboard is intentionally slow. You can adjust these parameters by adding these settings to your keymap's `config.h` file. All times are specified in milliseconds (ms). |
||||
|----------------|---------|-----------------| |
||||
|`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_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 | |
||||
|
||||
``` |
||||
#define MOUSEKEY_DELAY 300 |
||||
#define MOUSEKEY_INTERVAL 50 |
||||
#define MOUSEKEY_MAX_SPEED 10 |
||||
#define MOUSEKEY_TIME_TO_MAX 20 |
||||
#define MOUSEKEY_WHEEL_MAX_SPEED 8 |
||||
#define MOUSEKEY_WHEEL_TIME_TO_MAX 40 |
||||
``` |
||||
## Configuring mouse keys |
||||
|
||||
Mouse keys supports two different modes to move the cursor: |
||||
|
||||
* **Accelerated (default):** Holding movement keys accelerates the cursor until it reaches its maximum speed. |
||||
* **Constant:** Holding movement keys moves the cursor at constant speeds. |
||||
|
||||
The same principle applies to scrolling. |
||||
|
||||
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. |
||||
|
||||
### `MOUSEKEY_DELAY` |
||||
### Accelerated mode |
||||
|
||||
When one of the mouse movement buttons is pressed this setting is used to define the delay between that button press and the mouse cursor moving. Some people find that small movements are impossible if this setting is too low, while settings that are too high feel sluggish. |
||||
This is the default mode. You can adjust the cursor and scrolling acceleration using the following settings in your keymap’s `config.h` file: |
||||
|
||||
### `MOUSEKEY_INTERVAL` |
||||
|Define |Default|Description | |
||||
|----------------------------|-------|---------------------------------------------------------| |
||||
|`MOUSEKEY_DELAY` |300 |Delay between pressing a movement key and cursor movement| |
||||
|`MOUSEKEY_INTERVAL` |50 |Time between cursor movements | |
||||
|`MOUSEKEY_MAX_SPEED` |10 |Maximum cursor speed at which acceleration stops | |
||||
|`MOUSEKEY_TIME_TO_MAX` |20 |Time until maximum cursor speed is reached | |
||||
|`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 | |
||||
|
||||
When a movement key is held down this specifies how long to wait between each movement report. Lower settings will translate into an effectively higher mouse speed. |
||||
Tips: |
||||
|
||||
### `MOUSEKEY_MAX_SPEED` |
||||
* 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. |
||||
|
||||
As a movement key is held down the speed of the mouse cursor will increase until it reaches `MOUSEKEY_MAX_SPEED`. |
||||
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). |
||||
|
||||
### `MOUSEKEY_TIME_TO_MAX` |
||||
### Constant mode |
||||
|
||||
How long you want to hold down a movement key for until `MOUSEKEY_MAX_SPEED` is reached. This controls how quickly your cursor will accelerate. |
||||
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. |
||||
|
||||
### `MOUSEKEY_WHEEL_MAX_SPEED` |
||||
You can choose whether speed selection is momentary or tap-to-select: |
||||
|
||||
The top speed for scrolling movements. |
||||
* **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. |
||||
|
||||
### `MOUSEKEY_WHEEL_TIME_TO_MAX` |
||||
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 |
||||
``` |
||||
|
||||
How long you want to hold down a scroll key for until `MOUSEKEY_WHEEL_MAX_SPEED` is reached. This controls how quickly your scrolling will accelerate. |
||||
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`) | |
||||
|
@ -0,0 +1,289 @@ |
||||
# OLED Driver |
||||
|
||||
## OLED Supported Hardware |
||||
|
||||
OLED modules using SSD1306 or SH1106 driver ICs, communicating over I2C. |
||||
Tested combinations: |
||||
|
||||
| IC driver | Size | Keyboard Platform | Notes | |
||||
|-----------|--------|-------------------|--------------------------| |
||||
| SSD1306 | 128x32 | AVR | Primary support | |
||||
| SSD1306 | 128x64 | AVR | Verified working | |
||||
| SSD1306 | 128x32 | ARM | | |
||||
| SH1106 | 128x64 | AVR | No rotation or scrolling | |
||||
|
||||
Hardware configurations using ARM-based microcontrollers or different sizes of OLED modules may be compatible, but are untested. |
||||
|
||||
!> Warning: This OLED Driver currently uses the new i2c_master driver from split common code. If your split keyboard uses I2C to communicate between sides, this driver could cause an address conflict (serial is fine). Please contact your keyboard vendor and ask them to migrate to the latest split common code to fix this. In addition, the display timeout system to reduce OLED burn-in also uses split common to detect keypresses, so you will need to implement custom timeout logic for non-split common keyboards. |
||||
|
||||
## Usage |
||||
|
||||
To enable the OLED feature, there are three steps. First, when compiling your keyboard, you'll need to set `OLED_DRIVER_ENABLE=yes` in `rules.mk`, e.g.: |
||||
|
||||
``` |
||||
OLED_DRIVER_ENABLE = yes |
||||
``` |
||||
|
||||
This enables the feature and the `OLED_DRIVER_ENABLE` define. Then in your `keymap.c` file, you will need to implement the user task call, e.g: |
||||
|
||||
```C++ |
||||
#ifdef OLED_DRIVER_ENABLE |
||||
void oled_task_user(void) { |
||||
// Host Keyboard Layer Status |
||||
oled_write_P(PSTR("Layer: "), false); |
||||
switch (biton32(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 |
||||
uint8_t led_usb_state = host_keyboard_leds(); |
||||
oled_write_P(led_usb_state & (1<<USB_LED_NUM_LOCK) ? PSTR("NUMLCK ") : PSTR(" "), false); |
||||
oled_write_P(led_usb_state & (1<<USB_LED_CAPS_LOCK) ? PSTR("CAPLCK ") : PSTR(" "), false); |
||||
oled_write_P(led_usb_state & (1<<USB_LED_SCROLL_LOCK) ? PSTR("SCRLCK ") : PSTR(" "), false); |
||||
} |
||||
#endif |
||||
``` |
||||
|
||||
## Logo Example |
||||
|
||||
In the default font, ranges in the font file 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,0}; |
||||
|
||||
oled_write_P(qmk_logo, false); |
||||
} |
||||
``` |
||||
|
||||
## Other Examples |
||||
|
||||
In split keyboards, it is very common to have two OLED displays that each render different content and oriented flipped differently. You can do this by switching which content to render by using the return from `is_keyboard_master()` or `is_keyboard_left()` found in `split_util.h`, e.g: |
||||
|
||||
```C++ |
||||
#ifdef OLED_DRIVER_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; |
||||
} |
||||
|
||||
void 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 statuc logo |
||||
oled_scroll_left(); // Turns on scrolling |
||||
} |
||||
} |
||||
#endif |
||||
``` |
||||
|
||||
|
||||
## Basic Configuration |
||||
|
||||
| Define | Default | Description | |
||||
|------------------------|-------------------|----------------------------------------------------------------------------------------------------------------------------| |
||||
| `OLED_DISPLAY_ADDRESS` | `0x3C` | The i2c address of the OLED Display | |
||||
| `OLED_FONT_H` | `"glcdfont.c"` | The font code file to use for custom fonts | |
||||
| `OLED_FONT_START` | `0` | The starting characer index for custom fonts | |
||||
| `OLED_FONT_END` | `224` | The ending characer index for custom fonts | |
||||
| `OLED_FONT_WIDTH` | `6` | The font width | |
||||
| `OLED_FONT_HEIGHT` | `8` | The font height (untested) | |
||||
| `OLED_DISABLE_TIMEOUT` | *Not defined* | Disables the built in OLED timeout feature. Useful when implementing custom timeout rules. | |
||||
| `OLED_IC` | `OLED_IC_SSD1306` | Set to `OLED_IC_SH1106` if you're using the SH1106 OLED controller. | |
||||
| `OLED_COLUMN_OFFSET` | `0` | (SH1106 only.) Shift output to the right this many pixels.<br />Useful for 128x64 displays centered on a 132x64 SH1106 IC. | |
||||
|
||||
## 128x64 & Custom sized OLED Displays |
||||
|
||||
The default display size for this feature is 128x32 and all necessary defines are precalculated with that in mind. We have added a define, `OLED_DISPLAY_128X64`, to switch all the values to be used in a 128x64 display, as well as added a custom define, `OLED_DISPLAY_CUSTOM`, that allows you to provide the necessary values to the driver. |
||||
|
||||
|Define |Default |Description | |
||||
|-----------------------|---------------|-----------------------------------------------------------------| |
||||
|`OLED_DISPLAY_128X64` |*Not defined* |Changes the display defines for use with 128x64 displays. | |
||||
|`OLED_DISPLAY_CUSTOM` |*Not defined* |Changes the display defines for use with custom displays.<br />Requires user to implement the below defines. | |
||||
|`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.<br />`(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.<br />`(sizeof(OLED_BLOCK_TYPE) * 8)`. | |
||||
|`OLED_BLOCK_SIZE` |`32` |The size of each block for dirty rendering<br />`(OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)`. | |
||||
|`OLED_COM_PINS` |`COM_PINS_SEQ` |How the SSD1306 chip maps it's memory to display.<br />Options are `COM_PINS_SEQ`, `COM_PINS_ALT`, `COM_PINS_SEQ_LR`, & `COM_PINS_ALT_LR`. | |
||||
|`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 |
||||
|
||||
!> Rotation is unsupported on the SH1106. |
||||
|
||||
```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 drivers only natively support in hard ware 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 Rotated Rendering 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 OLED 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. |
||||
|
||||
## 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_user(oled_rotation_t rotation); |
||||
|
||||
// Clears the display buffer, resets cursor position to 0, and sets the buffer to dirty for rendering |
||||
void oled_clear(void); |
||||
|
||||
// Renders the dirty chunks of the buffer to OLED display |
||||
void oled_render(void); |
||||
|
||||
// Moves cursor to character position indicated by column and line, wraps if out of bounds |
||||
// Max column denoted by 'oled_max_chars()' and max lines by 'oled_max_lines()' functions |
||||
void oled_set_cursor(uint8_t col, uint8_t line); |
||||
|
||||
// Advances the cursor to the next page, writing ' ' if true |
||||
// Wraps to the begining when out of bounds |
||||
void oled_advance_page(bool clearPageRemainder); |
||||
|
||||
// Moves the cursor forward 1 character length |
||||
// Advance page if there is not enough room for the next character |
||||
// Wraps to the begining when out of bounds |
||||
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); |
||||
|
||||
// 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); |
||||
|
||||
// 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); |
||||
|
||||
// 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 |
||||
void oled_task_user(void); |
||||
|
||||
// Scrolls the entire display right |
||||
// Returns true if the screen was scrolling or starts scrolling |
||||
// NOTE: display contents cannot be changed while scrolling |
||||
bool oled_scroll_right(void); |
||||
|
||||
// Scrolls the entire display left |
||||
// 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 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); |
||||
``` |
||||
|
||||
!> Scrolling and rotation are unsupported on the SH1106. |
||||
|
||||
## 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` | |
@ -0,0 +1,60 @@ |
||||
# Space Cadet: The Future, Built In |
||||
|
||||
Steve Losh described the [Space Cadet Shift](http://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 `KC_LSPO` (Left Shift, Parenthesis Open), and Right Shift with `KC_RSPC` (Right Shift, Parenthesis Close). |
||||
- Replace the Left Control key with `KC_LCPO` (Left Control, Parenthesis Open), and Right Control with `KC_RCPC` (Right Control, Parenthesis Close). |
||||
- Replace the Left Alt key with `KC_LAPO` (Left Alt, Parenthesis Open), and Right Alt with `KC_RAPC` (Right Alt, Parenthesis Close). |
||||
- Replace any Shift key in your keymap with `KC_SFTENT` (Right Shift, Enter). |
||||
|
||||
## Keycodes |
||||
|
||||
|Keycode |Description | |
||||
|-----------|-------------------------------------------| |
||||
|`KC_LSPO` |Left Shift when held, `(` when tapped | |
||||
|`KC_RSPC` |Right Shift when held, `)` when tapped | |
||||
|`KC_LCPO` |Left Control when held, `(` when tapped | |
||||
|`KC_RCPC` |Right Control when held, `)` when tapped | |
||||
|`KC_LAPO` |Left Alt when held, `(` when tapped | |
||||
|`KC_RAPC` |Right Alt when held, `)` when tapped | |
||||
|`KC_SFTENT`|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.md) 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 `KC_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 `KC_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 | |
@ -1,33 +0,0 @@ |
||||
# Space Cadet Shift: The Future, Built In |
||||
|
||||
Steve Losh described the [Space Cadet Shift](http://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. |
||||
|
||||
## Usage |
||||
|
||||
Replace the Left Shift key in your keymap with `KC_LSPO` (Left Shift, Parenthesis Open), and Right Shift with `KC_RSPC` (Right Shift, Parenthesis Close). |
||||
|
||||
## Keycodes |
||||
|
||||
|Keycode |Description | |
||||
|---------|--------------------------------------| |
||||
|`KC_LSPO`|Left Shift when held, `(` when tapped | |
||||
|`KC_RSPC`|Right Shift when held, `)` when tapped| |
||||
|
||||
## Caveats |
||||
|
||||
Space Cadet's functionality can conflict with the default Command functionality when both Shift keys are held at the same time. 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`. |
||||
You can also disable the rollover, allowing you to use the opposite Shift key to cancel the Space Cadet state in the event of an erroneous press, instead of emitting a pair of parentheses when the keys are released. |
||||
|
||||
|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 | |
||||
|`DISABLE_SPACE_CADET_ROLLOVER`|*Not defined*|If defined, use the opposite Shift key to cancel Space Cadet| |
@ -1,31 +0,0 @@ |
||||
# Space Cadet Shift Enter |
||||
|
||||
Based on the [Space Cadet Shift](feature_space_cadet_shift.md) feature. Tap the Shift key on its own, and it behaves like Enter. When held, the Shift functions as normal. |
||||
|
||||
## Usage |
||||
|
||||
Replace any Shift key in your keymap with `KC_SFTENT` (Shift, Enter), and you're done. |
||||
|
||||
## Keycodes |
||||
|
||||
|Keycode |Description | |
||||
|-----------|----------------------------------------| |
||||
|`KC_SFTENT`|Right Shift when held, Enter when tapped| |
||||
|
||||
## Caveats |
||||
|
||||
As with Space Cadet Shift, this feature may conflict with Command, so it should be disabled in your `rules.mk` with: |
||||
|
||||
```make |
||||
COMMAND_ENABLE = no |
||||
``` |
||||
|
||||
This feature also uses the same timers as Space Cadet Shift, so using them in tandem may produce strange results. |
||||
|
||||
## Configuration |
||||
|
||||
By default Space Cadet assumes a US ANSI layout, but if you'd like to use a different key for Enter, you can redefine it in your `config.h`: |
||||
|
||||
|Define |Default |Description | |
||||
|------------|--------|------------------------------------------------| |
||||
|`SFTENT_KEY`|`KC_ENT`|The keycode to send when the Shift key is tapped| |
@ -0,0 +1,185 @@ |
||||
# 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 I<sup>2</sup>C 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. |
||||
|
||||
!> ARM is not yet supported for Split Keyboards. Progress is being made, but we are not quite there, yet. |
||||
|
||||
|
||||
## 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 I<sup>2</sup>C 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. |
||||
|
||||
!> 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 (aka PDO or pin 3) between the two Pro Micros. |
||||
|
||||
?> Note that the pin used here is actually set by `SOFT_SERIAL_PIN` below. |
||||
|
||||
 |
||||
|
||||
### I<sup>2</sup>C 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. It is also possible to use 4 resistors and have the pull-ups in both halves, but this is unnecessary in simple use cases. |
||||
|
||||
 |
||||
|
||||
## 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 |
||||
``` |
||||
|
||||
### 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. 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. |
||||
|
||||
#### 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 |
||||
``` |
||||
|
||||
However, you'll have to flash the EEPROM files for the correct hand to each controller. You can do this manually, or there are targets for avrdude and dfu to do this, while flashing the firmware: |
||||
|
||||
* `:avrdude-split-left` |
||||
* `:avrdude-split-right` |
||||
* `:dfu-split-left` |
||||
* `:dfu-split-right` |
||||
|
||||
This setting is not changed when re-initializing the EEPROM using the `EEP_RST` 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 set the handedness at compile time. This is done by adding the following to your `config.h` file: |
||||
|
||||
```c |
||||
#define MASTER_RIGHT |
||||
``` |
||||
|
||||
or |
||||
|
||||
```c |
||||
#define MASTER_LEFT |
||||
``` |
||||
|
||||
If neither 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 enables I<sup>2</sup>C support for split keyboards. This isn't strictly for communication, but can be used for OLED or other I<sup>2</sup>C-based devices. |
||||
|
||||
```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 I<sup>2</sup>C on the board, you will need to set this, and to something other than D0 and D1 (as these are used for I<sup>2</sup>C 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 |
||||
|
||||
### 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 { <row pins> } |
||||
#define MATRIX_COL_PINS_RIGHT { <col pins> } |
||||
``` |
||||
|
||||
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). |
||||
|
||||
|
||||
```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. |
||||
|
||||
?> This setting implies that `RGBLIGHT_SPLIT` is enabled, and will forcibly enable it, if it's not. |
||||
|
||||
|
||||
## 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. |
||||
|
||||
<!-- I may port this information later, but for now ... it's very nice, and covers everything --> |
@ -0,0 +1,30 @@ |
||||
# 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.: |
||||
|
||||
``` |
||||
BOOTMAGIC_ENABLE = no |
||||
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 VLK_TOG 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. |
@ -0,0 +1,121 @@ |
||||
# I2C Master Driver |
||||
|
||||
The I2C Master drivers used in QMK have a set of common functions to allow portability between MCUs. |
||||
|
||||
## Available functions |
||||
|
||||
|Function |Description | |
||||
|------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
||||
|`void i2c_init(void);` |Initializes the I2C driver. This function should be called once before any transaction is initiated. | |
||||
|`uint8_t i2c_start(uint8_t address);` |Starts an I2C transaction. Address is the 7-bit slave address without the direction bit. | |
||||
|`uint8_t i2c_transmit(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);` |Transmit data over I2C. Address is the 7-bit slave address without the direction. Returns status of transaction. | |
||||
|`uint8_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);` |Receive data over I2C. Address is the 7-bit slave address without the direction. Saves number of bytes specified by `length` in `data` array. Returns status of transaction. | |
||||
|`uint8_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);` |Same as the `i2c_transmit` function but `regaddr` sets where in the slave the data will be written. | |
||||
|`uint8_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);` |Same as the `i2c_receive` function but `regaddr` sets from where in the slave the data will be read. | |
||||
|`uint8_t i2c_stop(void);` |Ends an I2C transaction. | |
||||
|
||||
### Function Return |
||||
|
||||
All the above functions, except `void i2c_init(void);` return the following truth table: |
||||
|
||||
|Return Value |Description | |
||||
|---------------|---------------------------------------------------| |
||||
|0 |Operation executed successfully. | |
||||
|-1 |Operation failed. | |
||||
|-2 |Operation timed out. | |
||||
|
||||
|
||||
## AVR |
||||
|
||||
### Configuration |
||||
|
||||
The following defines can be used to configure the I2C master driver. |
||||
|
||||
|Variable |Description |Default| |
||||
|------------------|---------------------------------------------------|-------| |
||||
|`F_SCL` |Clock frequency in Hz |400KHz | |
||||
|`Prescaler` |Divides master clock to aid in I2C clock selection |1 | |
||||
|
||||
AVRs usually have set GPIO which turn into I2C pins, therefore no further configuration is required. |
||||
|
||||
## ARM |
||||
|
||||
For ARM the Chibios I2C HAL driver is under the hood. |
||||
This section assumes an STM32 MCU. |
||||
|
||||
### Configuration |
||||
|
||||
The configuration for ARM MCUs can be quite complex as often there are multiple I2C drivers which can be assigned to a variety of ports. |
||||
|
||||
Firstly the `mcuconf.h` file must be setup to enable the necessary hardware drivers. |
||||
|
||||
|Variable |Description |Default| |
||||
|------------------------------|------------------------------------------------------------------------------------|-------| |
||||
|`#STM32_I2C_USE_XXX` |Enable/Disable the hardware driver XXX (each driver should be explicitly listed) |FALSE | |
||||
|`#STM32_I2C_BUSY_TIMEOUT` |Time in ms 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 | |
||||
|
||||
Secondly, in the `halconf.h` file, `#define HAL_USE_I2C` must be set to `TRUE`. This allows ChibiOS to load its I2C driver. |
||||
|
||||
Lastly, we need to assign the correct GPIO pins depending on the I2C hardware driver we want to use. |
||||
|
||||
By default the I2C1 hardware driver is assumed to be used. If another hardware driver is used, `#define I2C_DRIVER I2CDX` should be added to the `config.h` file with X being the number of hardware driver used. For example is I2C3 is enabled, the `config.h` file should contain `#define I2C_DRIVER I2CD3`. This aligns the QMK I2C driver with the Chibios I2C driver. |
||||
|
||||
STM32 MCUs allows a variety of pins to be configured as I2C pins depending on the hardware driver used. By default B6 and B7 are set to I2C. You can use these defines to set your i2c pins: |
||||
|
||||
| Variable | Description | Default | |
||||
|--------------------------|----------------------------------------------------------------------------------------------|---------| |
||||
| `I2C1_SCL_BANK` | The bank of pins (`GPIOA`, `GPIOB`, `GPIOC`) to use for SCL | `GPIOB` | |
||||
| `I2C1_SDA_BANK` | The bank of pins (`GPIOA`, `GPIOB`, `GPIOC`) to use for SDA | `GPIOB` | |
||||
| `I2C1_SCL` | The pin number for the SCL pin (0-9) | `6` | |
||||
| `I2C1_SDA` | The pin number for the SDA pin (0-9) | `7` | |
||||
| `I2C1_BANK` (deprecated) | The bank of pins (`GPIOA`, `GPIOB`, `GPIOC`), superceded by `I2C1_SCL_BANK`, `I2C1_SDA_BANK` | `GPIOB` | |
||||
|
||||
The ChibiOS I2C driver configuration depends on STM32 MCU: |
||||
|
||||
STM32F1xx, STM32F2xx, STM32F4xx, STM32L0xx and STM32L1xx use I2Cv1; |
||||
STM32F0xx, STM32F3xx, STM32F7xx and STM32L4xx use I2Cv2; |
||||
|
||||
#### I2Cv1 |
||||
STM32 MCUs allow for different clock and duty parameters when configuring I2Cv1. These can be modified using the following parameters, using <https://www.playembedded.org/blog/stm32-i2c-chibios/#I2Cv1_configuration_structure> as a reference: |
||||
|
||||
| Variable | Default | |
||||
|--------------------|------------------| |
||||
| `I2C1_OPMODE` | `OPMODE_I2C` | |
||||
| `I2C1_CLOCK_SPEED` | `100000` | |
||||
| `I2C1_DUTY_CYCLE` | `STD_DUTY_CYCLE` | |
||||
|
||||
#### I2Cv2 |
||||
STM32 MCUs allow for different timing parameters when configuring I2Cv2. These can be modified using the following parameters, using <https://www.st.com/en/embedded-software/stsw-stm32126.html> as a reference: |
||||
|
||||
| Variable | Default | |
||||
|-----------------------|---------| |
||||
| `I2C1_TIMINGR_PRESC` | `15U` | |
||||
| `I2C1_TIMINGR_SCLDEL` | `4U` | |
||||
| `I2C1_TIMINGR_SDADEL` | `2U` | |
||||
| `I2C1_TIMINGR_SCLH` | `15U` | |
||||
| `I2C1_TIMINGR_SCLL` | `21U` | |
||||
|
||||
STM32 MCUs allow for different "alternate function" modes when configuring GPIO pins. These are required to switch the pins used to I2Cv2 mode. See the respective datasheet for the appropriate values for your MCU. |
||||
|
||||
| Variable | Default | |
||||
|---------------------|---------| |
||||
| `I2C1_SCL_PAL_MODE` | `4` | |
||||
| `I2C1_SDA_PAL_MODE` | `4` | |
||||
|
||||
#### Other |
||||
You can also overload the `void i2c_init(void)` function, which has a weak attribute. If you do this the configuration variables above will not be used. Please consult the datasheet of your MCU for the available GPIO configurations. The following is an example initialization function: |
||||
|
||||
```C |
||||
void i2c_init(void) |
||||
{ |
||||
setPinInput(B6); // Try releasing special pins for a short time |
||||
setPinInput(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 |
||||
} |
||||
``` |
@ -1,261 +0,0 @@ |
||||
Keycode Symbol Table |
||||
==================== |
||||
Keycodes are defined in `common/keycode.h`. |
||||
Range of 00-A4 and E0-E7 are identical with HID Usage: |
||||
<http://www.usb.org/developers/hidpage/Hut1_12v2.pdf> |
||||
Virtual keycodes are defined out of above range to support special actions. |
||||
|
||||
|
||||
Keycode Symbol Short name Description |
||||
-------------------------------------------------------------------------------- |
||||
KC_NO 00 Reserved (no event indicated) |
||||
KC_ROLL_OVER 01 Keyboard ErrorRollOver |
||||
KC_POST_FAIL 02 Keyboard POSTFail |
||||
KC_UNDEFINED 03 Keyboard ErrorUndefined |
||||
KC_A 04 Keyboard a and A |
||||
KC_B 05 Keyboard b and B |
||||
KC_C 06 Keyboard c and C |
||||
KC_D 07 Keyboard d and D |
||||
KC_E 08 Keyboard e and E |
||||
KC_F 09 Keyboard f and F |
||||
KC_G 0A Keyboard g and G |
||||
KC_H 0B Keyboard h and H |
||||
KC_I 0C Keyboard i and I |
||||
KC_J 0D Keyboard j and J |
||||
KC_K 0E Keyboard k and K |
||||
KC_L 0F Keyboard l and L |
||||
KC_M 10 Keyboard m and M |
||||
KC_N 11 Keyboard n and N |
||||
KC_O 12 Keyboard o and O |
||||
KC_P 13 Keyboard p and P |
||||
KC_Q 14 Keyboard q and Q |
||||
KC_R 15 Keyboard r and R |
||||
KC_S 16 Keyboard s and S |
||||
KC_T 17 Keyboard t and T |
||||
KC_U 18 Keyboard u and U |
||||
KC_V 19 Keyboard v and V |
||||
KC_W 1A Keyboard w and W |
||||
KC_X 1B Keyboard x and X |
||||
KC_Y 1C Keyboard y and Y |
||||
KC_Z 1D Keyboard z and Z |
||||
KC_1 1E Keyboard 1 and ! |
||||
KC_2 1F Keyboard 2 and @ |
||||
KC_3 20 Keyboard 3 and # |
||||
KC_4 21 Keyboard 4 and $ |
||||
KC_5 22 Keyboard 5 and % |
||||
KC_6 23 Keyboard 6 and ^ |
||||
KC_7 24 Keyboard 7 and & |
||||
KC_8 25 Keyboard 8 and * |
||||
KC_9 26 Keyboard 9 and ( |
||||
KC_0 27 Keyboard 0 and ) |
||||
KC_ENTER KC_ENT 28 Keyboard Return (ENTER) |
||||
KC_ESCAPE KC_ESC 29 Keyboard ESCAPE |
||||
KC_BSPACE KC_BSPC 2A Keyboard DELETE (Backspace) |
||||
KC_TAB 2B Keyboard Tab |
||||
KC_SPACE KC_SPC 2C Keyboard Spacebar |
||||
KC_MINUS KC_MINS 2D Keyboard - and (underscore) |
||||
KC_EQUAL KC_EQL 2E Keyboard = and + |
||||
KC_LBRACKET KC_LBRC 2F Keyboard [ and { |
||||
KC_RBRACKET KC_RBRC 30 Keyboard ] and } |
||||
KC_BSLASH KC_BSLS 31 Keyboard \ and | |
||||
KC_NONUS_HASH KC_NUHS 32 Keyboard Non-US # and ~ |
||||
KC_SCOLON KC_SCLN 33 Keyboard ; and : |
||||
KC_QUOTE KC_QUOT 34 Keyboard ‘ and “ |
||||
KC_GRAVE KC_GRV 35 Keyboard Grave Accent and Tilde |
||||
KC_COMMA KC_COMM 36 Keyboard , and < |
||||
KC_DOT 37 Keyboard . and > |
||||
KC_SLASH KC_SLSH 38 Keyboard / and ? |
||||
KC_CAPSLOCK KC_CAPS 39 Keyboard Caps Lock |
||||
KC_F1 3A Keyboard F1 |
||||
KC_F2 3B Keyboard F2 |
||||
KC_F3 3C Keyboard F3 |
||||
KC_F4 3D Keyboard F4 |
||||
KC_F5 3E Keyboard F5 |
||||
KC_F6 3F Keyboard F6 |
||||
KC_F7 40 Keyboard F7 |
||||
KC_F8 41 Keyboard F8 |
||||
KC_F9 42 Keyboard F9 |
||||
KC_F10 43 Keyboard F10 |
||||
KC_F11 44 Keyboard F11 |
||||
KC_F12 45 Keyboard F12 |
||||
KC_PSCREEN KC_PSCR 46 Keyboard PrintScreen |
||||
KC_SCROLLLOCK KC_SLCK 47 Keyboard Scroll Lock |
||||
KC_PAUSE KC_PAUS 48 Keyboard Pause |
||||
KC_INSERT KC_INS 49 Keyboard Insert |
||||
KC_HOME 4A Keyboard Home |
||||
KC_PGUP 4B Keyboard PageUp |
||||
KC_DELETE KC_DEL 4C Keyboard Delete Forward |
||||
KC_END 4D Keyboard End |
||||
KC_PGDOWN KC_PGDN 4E Keyboard PageDown |
||||
KC_RIGHT KC_RGHT 4F Keyboard RightArrow |
||||
KC_LEFT 50 Keyboard LeftArrow |
||||
KC_DOWN 51 Keyboard DownArrow |
||||
KC_UP 52 Keyboard UpArrow |
||||
KC_NUMLOCK KC_NLCK 53 Keypad Num Lock and Clear |
||||
KC_KP_SLASH KC_PSLS 54 Keypad / |
||||
KC_KP_ASTERISK KC_PAST 55 Keypad * |
||||
KC_KP_MINUS KC_PMNS 56 Keypad - |
||||
KC_KP_PLUS KC_PPLS 57 Keypad + |
||||
KC_KP_ENTER KC_PENT 58 Keypad ENTER |
||||
KC_KP_1 KC_P1 59 Keypad 1 and End |
||||
KC_KP_2 KC_P2 5A Keypad 2 and Down Arrow |
||||
KC_KP_3 KC_P3 5B Keypad 3 and PageDn |
||||
KC_KP_4 KC_P4 5C Keypad 4 and Left Arrow |
||||
KC_KP_5 KC_P5 5D Keypad 5 |
||||
KC_KP_6 KC_P6 5E Keypad 6 and Right Arrow |
||||
KC_KP_7 KC_P7 5F Keypad 7 and Home |
||||
KC_KP_8 KC_P8 60 Keypad 8 and Up Arrow |
||||
KC_KP_9 KC_P9 61 Keypad 9 and PageUp |
||||
KC_KP_0 KC_P0 62 Keypad 0 and Insert |
||||
KC_KP_DOT KC_PDOT 63 Keypad . and Delete |
||||
KC_NONUS_BSLASH KC_NUBS 64 Keyboard Non-US \ and | |
||||
KC_APPLICATION KC_APP 65 Keyboard Application |
||||
KC_POWER 66 Keyboard Power |
||||
KC_KP_EQUAL KC_PEQL 67 Keypad = |
||||
KC_F13 68 Keyboard F13 |
||||
KC_F14 69 Keyboard F14 |
||||
KC_F15 6A Keyboard F15 |
||||
KC_F16 6B Keyboard F16 |
||||
KC_F17 6C Keyboard F17 |
||||
KC_F18 6D Keyboard F18 |
||||
KC_F19 6E Keyboard F19 |
||||
KC_F20 6F Keyboard F20 |
||||
KC_F21 70 Keyboard F21 |
||||
KC_F22 71 Keyboard F22 |
||||
KC_F23 72 Keyboard F23 |
||||
KC_F24 73 Keyboard F24 |
||||
KC_EXECUTE 74 Keyboard Execute |
||||
KC_HELP 75 Keyboard Help |
||||
KC_MENU 76 Keyboard Menu |
||||
KC_SELECT 77 Keyboard Select |
||||
KC_STOP 78 Keyboard Stop |
||||
KC_AGAIN 79 Keyboard Again |
||||
KC_UNDO 7A Keyboard Undo |
||||
KC_CUT 7B Keyboard Cut |
||||
KC_COPY 7C Keyboard Copy |
||||
KC_PASTE 7D Keyboard Paste |
||||
KC_FIND 7E Keyboard Find |
||||
KC__MUTE 7F Keyboard Mute |
||||
KC__VOLUP 80 Keyboard Volume Up |
||||
KC__VOLDOWN 81 Keyboard Volume Down |
||||
KC_LOCKING_CAPS 82 Keyboard Locking Caps Lock |
||||
KC_LOCKING_NUM 83 Keyboard Locking Num Lock |
||||
KC_LOCKING_SCROLL 84 Keyboard Locking Scroll Lock |
||||
KC_KP_COMMA KC_PCMM 85 Keypad Comma |
||||
KC_KP_EQUAL_AS400 86 Keypad Equal Sign |
||||
KC_INT1 KC_RO 87 Keyboard International115 |
||||
KC_INT2 KC_KANA 88 Keyboard International216 |
||||
KC_INT3 KC_JYEN 89 Keyboard International317 |
||||
KC_INT4 KC_HENK 8A Keyboard International418 |
||||
KC_INT5 KC_MHEN 8B Keyboard International519 |
||||
KC_INT6 8C Keyboard International620 |
||||
KC_INT7 8D Keyboard International721 |
||||
KC_INT8 8E Keyboard International822 |
||||
KC_INT9 8F Keyboard International922 |
||||
KC_LANG1 90 Keyboard LANG125 |
||||
KC_LANG2 91 Keyboard LANG226 |
||||
KC_LANG3 92 Keyboard LANG330 |
||||
KC_LANG4 93 Keyboard LANG431 |
||||
KC_LANG5 94 Keyboard LANG532 |
||||
KC_LANG6 95 Keyboard LANG68 |
||||
KC_LANG7 96 Keyboard LANG78 |
||||
KC_LANG8 97 Keyboard LANG88 |
||||
KC_LANG9 98 Keyboard LANG98 |
||||
KC_ALT_ERASE 99 Keyboard Alternate Erase |
||||
KC_SYSREQ 9A Keyboard SysReq/Attention |
||||
KC_CANCEL 9B Keyboard Cancel |
||||
KC_CLEAR 9C Keyboard Clear |
||||
KC_PRIOR 9D Keyboard Prior |
||||
KC_RETURN 9E Keyboard Return |
||||
KC_SEPARATOR 9F Keyboard Separator |
||||
KC_OUT A0 Keyboard Out |
||||
KC_OPER A1 Keyboard Oper |
||||
KC_CLEAR_AGAIN A2 Keyboard Clear/Again |
||||
KC_CRSEL A3 Keyboard CrSel/Props |
||||
KC_EXSEL A4 Keyboard ExSel |
||||
/* Modifiers */ |
||||
KC_LCTRL KC_LCTL E0 Keyboard LeftControl |
||||
KC_LSHIFT KC_LSFT E1 Keyboard LeftShift |
||||
KC_LALT E2 Keyboard LeftAlt |
||||
KC_LGUI E3 Keyboard Left GUI(Windows/Apple/Meta key) |
||||
KC_RCTRL KC_RCTL E4 Keyboard RightControl |
||||
KC_RSHIFT KC_RSFT E5 Keyboard RightShift |
||||
KC_RALT E6 Keyboard RightAlt |
||||
KC_RGUI E7 Keyboard Right GUI(Windows/Apple/Meta key) |
||||
|
||||
/* |
||||
* Virtual keycodes |
||||
*/ |
||||
/* System Control */ |
||||
KC_SYSTEM_POWER KC_PWR System Power Down |
||||
KC_SYSTEM_SLEEP KC_SLEP System Sleep |
||||
KC_SYSTEM_WAKE KC_WAKE System Wake |
||||
/* Consumer Page */ |
||||
KC_AUDIO_MUTE KC_MUTE |
||||
KC_AUDIO_VOL_UP KC_VOLU |
||||
KC_AUDIO_VOL_DOWN KC_VOLD |
||||
KC_MEDIA_NEXT_TRACK KC_MNXT |
||||
KC_MEDIA_PREV_TRACK KC_MPRV |
||||
KC_MEDIA_STOP KC_MSTP |
||||
KC_MEDIA_PLAY_PAUSE KC_MPLY |
||||
KC_MEDIA_SELECT KC_MSEL |
||||
KC_MAIL KC_MAIL |
||||
KC_CALCULATOR KC_CALC |
||||
KC_MY_COMPUTER KC_MYCM |
||||
KC_WWW_SEARCH KC_WSCH |
||||
KC_WWW_HOME KC_WHOM |
||||
KC_WWW_BACK KC_WBAK |
||||
KC_WWW_FORWARD KC_WFWD |
||||
KC_WWW_STOP KC_WSTP |
||||
KC_WWW_REFRESH KC_WREF |
||||
KC_WWW_FAVORITES KC_WFAV |
||||
/* Mousekey */ |
||||
KC_MS_UP KC_MS_U Mouse Cursor Up |
||||
KC_MS_DOWN KC_MS_D Mouse Cursor Down |
||||
KC_MS_LEFT KC_MS_L Mouse Cursor Left |
||||
KC_MS_RIGHT KC_MS_R Mouse Cursor Right |
||||
KC_MS_BTN1 KC_BTN1 Mouse Button 1 |
||||
KC_MS_BTN2 KC_BTN2 Mouse Button 2 |
||||
KC_MS_BTN3 KC_BTN3 Mouse Button 3 |
||||
KC_MS_BTN4 KC_BTN4 Mouse Button 4 |
||||
KC_MS_BTN5 KC_BTN5 Mouse Button 5 |
||||
KC_MS_WH_UP KC_WH_U Mouse Wheel Up |
||||
KC_MS_WH_DOWN KC_WH_D Mouse Wheel Down |
||||
KC_MS_WH_LEFT KC_WH_L Mouse Wheel Left |
||||
KC_MS_WH_RIGHT KC_WH_R Mouse Wheel Right |
||||
KC_MS_ACCEL0 KC_ACL0 Mouse Acceleration 0 |
||||
KC_MS_ACCEL1 KC_ACL1 Mouse Acceleration 1 |
||||
KC_MS_ACCEL2 KC_ACL2 Mouse Acceleration 2 |
||||
/* Fn key */ |
||||
KC_FN0 |
||||
KC_FN1 |
||||
KC_FN2 |
||||
KC_FN3 |
||||
KC_FN4 |
||||
KC_FN5 |
||||
KC_FN6 |
||||
KC_FN7 |
||||
KC_FN8 |
||||
KC_FN9 |
||||
KC_FN10 |
||||
KC_FN11 |
||||
KC_FN12 |
||||
KC_FN13 |
||||
KC_FN14 |
||||
KC_FN15 |
||||
KC_FN16 |
||||
KC_FN17 |
||||
KC_FN18 |
||||
KC_FN19 |
||||
KC_FN20 |
||||
KC_FN21 |
||||
KC_FN22 |
||||
KC_FN23 |
||||
KC_FN24 |
||||
KC_FN25 |
||||
KC_FN26 |
||||
KC_FN27 |
||||
KC_FN28 |
||||
KC_FN29 |
||||
KC_FN30 |
||||
KC_FN31 |
@ -0,0 +1,105 @@ |
||||
# QMK Configurator |
||||
|
||||
The [QMK Configurator](https://config.qmk.fm) is an online graphical user interface that generates QMK Firmware hex files. |
||||
|
||||
?> **Please follow these steps in order.** |
||||
|
||||
Watch the [Video Tutorial](https://youtu.be/tx54jkRC9ZY) |
||||
|
||||
The QMK Configurator works best with Chrome/Firefox. |
||||
|
||||
|
||||
!> **Files from other tools such as KLE, or kbfirmware will not be compatible with QMK Configurator. Do not load them, do not import them. QMK Configurator is a DIFFERENT tool. ** |
||||
|
||||
## Selecting your keyboard |
||||
|
||||
Click the drop down box and select the keyboard you want to create a keymap for. |
||||
|
||||
?> If your keyboard has several versions, make sure you select the correct one.** |
||||
|
||||
I'll say that again because it's important |
||||
|
||||
!> **MAKE SURE YOU SELECT THE RIGHT VERSION!** |
||||
|
||||
If your keyboard has been advertised to be powered by QMK but is not in the list, chances are a developer hasn't gotten to it yet or we haven't had a chance to merge it in yet. File an issue at [qmk_firmware](https://github.com/qmk/qmk_firmware/issues) requesting to support that particular keyboard, if there is no active [Pull Request](https://github.com/qmk/qmk_firmware/pulls?q=is%3Aopen+is%3Apr+label%3Akeyboard) for it. There are also QMK powered keyboards that are in their manufacturer's own github accounts. Double check for that as well. |
||||
|
||||
## Selecting your keyboard layout |
||||
|
||||
Choose the layout that best represents the keymap you want to create. Some keyboards do not have enough layouts or correct layouts defined yet. They will be supported in the future. |
||||
|
||||
## Keymap Name |
||||
|
||||
Call this keymap what you want. |
||||
|
||||
?> If you are running into issues when compiling, it may be worth changing this name, as it may already exist in the QMK Firmware repo. |
||||
|
||||
## Creating Your Keymap |
||||
|
||||
Keycode Entry is accomplished in 3 ways. |
||||
1. Drag and dropping |
||||
2. Clicking on an empty spot on the layout and clicking the keycode you desire |
||||
3. Clicking on an empty spot on the layout, pressing the physical key on your keyboard. |
||||
|
||||
Hover your mouse over a key and a short blurb will tell you what that keycode does. For a more verbose description please see |
||||
|
||||
[Basic Keycode Reference](https://docs.qmk.fm/#/keycodes_basic) |
||||
[Advanced Keycode Reference](https://docs.qmk.fm/#/feature_advanced_keycodes) |
||||
|
||||
In the event that you can't find a layout that supports your keymap, for example three spots for spacebar, or two spots for backspace, or 2 spots for shift etc etc, Fill them ALL up. |
||||
|
||||
### Example: |
||||
|
||||
3 spots for spacebar: Fill them ALL with spacebar |
||||
|
||||
2 spots for backspace: Fill them BOTH with backspace |
||||
|
||||
2 spots for right shift: Fill them BOTH with right shift |
||||
|
||||
1 spot for left shift and 1 spot for iso support: Fill them both with left shift |
||||
|
||||
5 spots, but only 4 keys: Guess and check or ask someone who has done it before. |
||||
|
||||
## Saving Your Keymap for Future Edits |
||||
|
||||
When you're satisfied with your keymap or just want to work on it later, press the `Export Keymap` button. It will save your keymap as the name you chose above appended with .json. |
||||
|
||||
You can then load this .json file in the future by pressing the `Import Keymap` button. |
||||
|
||||
!> **CAUTION:** This is not the same type of .json file used for kbfirmware.com or any other tool. If you try to use this for those tools, or the .json from those tools with QMK Configurator, there is a chance your keyboard will **explode**. |
||||
|
||||
## Generating your firmware file |
||||
|
||||
Press the green `Compile` button. |
||||
|
||||
When the compilation is done, you will be able to press the green `Download Firmware` button. |
||||
|
||||
## Flashing Your Keyboard |
||||
|
||||
Please refer to [Flashing Firmware](newbs_flashing.md) |
||||
|
||||
## Troubleshooting |
||||
|
||||
#### My .json file is not working |
||||
|
||||
If the .json file was generated with QMK Configurator, congratulations you have stumbled upon a bug. File an issue at [qmk_configurator](https://github.com/qmk/qmk_configurator/issues) |
||||
|
||||
If not....how did you miss my big bold message at the top saying not to use other .json files? |
||||
|
||||
#### There are extra spaces in my layout? What do I do? |
||||
|
||||
If you're referring to having three spots for space bar, the best course of action is to just fill them all with space bar. The same can be done for backspace and shifts |
||||
|
||||
#### What is the keycode for....... |
||||
|
||||
Please see |
||||
|
||||
[Basic Keycode Reference](https://docs.qmk.fm/#/keycodes_basic) |
||||
[Advanced Keycode Reference](https://docs.qmk.fm/#/feature_advanced_keycodes) |
||||
|
||||
#### It won't compile |
||||
|
||||
Please double check the other layers of your keymap to make sure there are no random keys present. |
||||
|
||||
## Problems and Bugs |
||||
|
||||
We are always accepting customer requests and bug reports. Please file them at [qmk_configurator](https://github.com/qmk/qmk_configurator/issues) |
@ -1,99 +1,102 @@ |
||||
# Introduction |
||||
|
||||
Your computer keyboard has a processor inside of it, not unlike the one inside your computer. This processor runs software that is responsible for detecting button presses and sending reports about the state of the keyboard when they are pressed or released. QMK fills the role of that software, detecting button presses and passing that information on to the host computer. When you build your custom layout you are creating the equivalent of an .exe for your keyboard. |
||||
Your computer keyboard has a processor inside of it, not unlike the one inside your computer. This processor runs software that is responsible for detecting button presses and sending reports about the state of the keyboard when buttons are pressed or released. QMK fills the role of that software, detecting button presses and passing that information on to the host computer. When you build your custom keymap, you are creating the equivalent of an executable program for your keyboard. |
||||
|
||||
QMK tries to put a lot of power into your hands by making easy things easy, and hard things possible. You don't have to know how to program to create powerful layouts, you only have to follow a few simple syntax rules. |
||||
QMK tries to put a lot of power into your hands by making easy things easy, and hard things possible. You don't have to know how to program to create powerful keymaps — you only have to follow a few simple syntax rules. |
||||
|
||||
# Getting Started |
||||
|
||||
Before you can build keymaps you need to install some software and setup your build environment. This only has to be done one time no matter how many keyboards you want to compile firmware for. |
||||
Before you can build keymaps, you need to install some software and set up your build environment. This only has to be done once no matter how many keyboards you plan to compile firmware for. |
||||
|
||||
If you would prefer a more graphical user interface approach, please consider using the online [QMK Configurator](https://config.qmk.fm). Please refer to [Building Your First Firmware using the online GUI](newbs_building_firmware_configurator.md). |
||||
|
||||
|
||||
## Download Software |
||||
|
||||
### Text Editor |
||||
|
||||
You'll need a program that can edit and save **plain text** files. If you are on Windows you can make due with Notepad, and on Linux you can use Gedit, both of which are simple but functional text editors. On macOS be careful with TextEdit.app, it will not save plain text files unless you make sure to select "Make Plain text" from the "Format" menu, or you can use another program such as Sublime Text. |
||||
You'll need a program that can edit and save **plain text** files. If you're on Windows you can make do with Notepad, and on Linux you can use gedit. Both of these are simple but functional text editors. On macOS, be careful with the default TextEdit app: it will not save plain text files unless you explicitly select _Make Plain Text_ from the _Format_ menu. |
||||
|
||||
You can also download and install a dedicated text editor like [Sublime Text](https://www.sublimetext.com/) or [VS Code](https://code.visualstudio.com/). This is probably the best way to go regardless of platform, as these programs are specifically made for editing code. |
||||
|
||||
?> Not sure which text editor to use? Laurence Bradford wrote [a great introduction](https://learntocodewith.me/programming/basics/text-editors/) to the subject. |
||||
|
||||
### QMK Toolbox |
||||
|
||||
QMK Toolbox is an optional graphical Windows and macOS program that allows you to both program and debug your custom keyboard. You will likely prefer it to easily flash your keyboard and receive the debugging messages that your keyboard will print. |
||||
QMK Toolbox is an optional graphical program for Windows and macOS that allows you to both program and debug your custom keyboard. You will likely find it invaluable for easily flashing your keyboard and viewing debug messages that it prints. |
||||
|
||||
Download the files from the links below: |
||||
[Download the latest release here.](https://github.com/qmk/qmk_toolbox/releases/latest) |
||||
|
||||
For Windows: "qmk_toolbox.exe" or "qmk_toolbox_install.exe" (with installer) |
||||
* For Windows: `qmk_toolbox.exe` (portable) or `qmk_toolbox_install.exe` (installer) |
||||
* For macOS: `QMK.Toolbox.app.zip` (portable) or `QMK.Toolbox.pkg` (installer) |
||||
|
||||
For Mac: "QMK.Toolbox.app.zip" or "QMK.Toolbox.pkg" (with installer) |
||||
## Set Up Your Environment |
||||
|
||||
* [Newest Release](https://github.com/qmk/qmk_toolbox/releases/latest) |
||||
* [Source Code](https://github.com/qmk/qmk_toolbox/) |
||||
We've tried to make QMK as easy to set up as possible. You only have to prepare your Linux or Unix environment, then let QMK install the rest. |
||||
|
||||
## Environment Setup |
||||
|
||||
We've tried to make QMK as easy to setup as possible. You only have to prepare your Linux or Unix environment and let QMK install the rest. |
||||
|
||||
?> If you haven't worked with the Linux/Unix command line before there are a few basic concepts and commands you should learn. These resources will teach you enough to work with QMK:<br> |
||||
?> If you haven't worked with the Linux/Unix command line before, there are a few basic concepts and commands you should learn. These resources will teach you enough to be able to work with QMK:<br> |
||||
[Must Know Linux Commands](https://www.guru99.com/must-know-linux-commands.html)<br> |
||||
[Some Basic Unix Commands](https://www.tjhsst.edu/~dhyatt/superap/unixcmd.html) |
||||
|
||||
### Windows |
||||
|
||||
You will need to install msys2 and git. |
||||
You will need to install MSYS2 and Git. |
||||
|
||||
* Follow the installation instructions on the msys2 homepage: http://www.msys2.org |
||||
* Close any open msys2 terminals, and open a new terminal |
||||
* Install git by running this command: `pacman -S git` |
||||
* Follow the installation instructions on the [MSYS2 homepage](http://www.msys2.org). |
||||
* Close any open MSYS2 terminals and open a new MSYS2 MinGW 64-bit terminal. |
||||
* Install Git by running this command: `pacman -S git`. |
||||
|
||||
### macOS |
||||
|
||||
You will need to install homebrew. Follow the instructions on the homebrew homepage: https://brew.sh |
||||
You will need to install Homebrew. Follow the instructions on the [Homebrew homepage](https://brew.sh). |
||||
|
||||
After homebrew is installed continue with "Download QMK", following step "Setup QMK" runs a script that will install other packages. |
||||
After Homebrew is installed, continue with _Set Up QMK_. In that step you will run a script that will install other packages. |
||||
|
||||
### Linux |
||||
|
||||
You will need to install git. It's extremely likely you already have it, but if not one of the following commands should install it: |
||||
You will need to install Git. It's very likely that you already have it, but if not, one of the following commands should install it: |
||||
|
||||
* Debian/Ubuntu/Devuan: `apt-get install git` |
||||
* Fedora/Redhat/Centos: `yum install git` |
||||
* Debian / Ubuntu / Devuan: `apt-get install git` |
||||
* Fedora / Red Hat / CentOS: `yum install git` |
||||
* Arch: `pacman -S git` |
||||
|
||||
## Download QMK |
||||
?> Docker is also an option on all platforms. [Click here for details.](getting_started_build_tools.md#docker) |
||||
|
||||
Once you have setup your Linux/Unix environment you are ready to download QMK. We will do this by using git to "clone" the QMK repository. Open a Terminal or MSYS2 Console window and leave it open for the remainder of this guide. Inside that window run these two commands: |
||||
## Set Up QMK |
||||
|
||||
git clone https://github.com/qmk/qmk_firmware.git |
||||
cd qmk_firmware |
||||
Once you have set up your Linux/Unix environment, you are ready to download QMK. We will do this by using Git to "clone" the QMK repository. Open a Terminal or MSYS2 MinGW window and leave it open for the remainder of this guide. Inside that window run these two commands: |
||||
|
||||
?> If you already know [how to use GitHub](getting_started_github.md) we recommend you create and clone your own fork instead. If you don't know what that means you can safely ignore this message. |
||||
```shell |
||||
git clone --recurse-submodules https://github.com/qmk/qmk_firmware.git |
||||
cd qmk_firmware |
||||
``` |
||||
|
||||
## Setup QMK |
||||
?> If you already know [how to use GitHub](getting_started_github.md), we recommend that you create and clone your own fork instead. If you don't know what that means, you can safely ignore this message. |
||||
|
||||
QMK comes with a script to help you setup the rest of what you'll need. You should run it now by typing in this command: |
||||
QMK comes with a script to help you set up the rest of what you'll need. You should run it now by typing in this command: |
||||
|
||||
./util/qmk_install.sh |
||||
util/qmk_install.sh |
||||
|
||||
## Test Your Build Environment |
||||
|
||||
Now that your QMK build environment is setup you can build a firmware for your keyboard. Start by trying to build the default layout for your keyboard. You should be able to do that with a command in this format: |
||||
Now that your QMK build environment is set up, you can build a firmware for your keyboard. Start by trying to build the keyboard's default keymap. You should be able to do that with a command in this format: |
||||
|
||||
make <keyboard>:default |
||||
|
||||
For example, to build a firmware for a Clueboard 66% use: |
||||
For example, to build a firmware for a Clueboard 66% you would use: |
||||
|
||||
make clueboard/66/rev3:default |
||||
|
||||
When it is done you should have a lot of output that ends similar to this: |
||||
|
||||
``` |
||||
Linking: .build/clueboard_66_rev2_default.elf [OK] |
||||
Creating load file for flashing: .build/clueboard_66_rev2_default.hex [OK] |
||||
Copying clueboard_66_rev2_default.hex to qmk_firmware folder [OK] |
||||
Checking file size of clueboard_66_rev2_default.hex [OK] |
||||
* File size is fine - 25174/28672 |
||||
Linking: .build/clueboard_66_rev3_default.elf [OK] |
||||
Creating load file for flashing: .build/clueboard_66_rev3_default.hex [OK] |
||||
Copying clueboard_66_rev3_default.hex to qmk_firmware folder [OK] |
||||
Checking file size of clueboard_66_rev3_default.hex [OK] |
||||
* The firmware size is fine - 26356/28672 (2316 bytes free) |
||||
``` |
||||
|
||||
## Creating Your Layout |
||||
# Creating Your Keymap |
||||
|
||||
Now you are ready to create your own personal layout. Move on to [Building Your First Firmware](newbs_building_firmware.md) for that. |
||||
You are now ready to create your own personal keymap! Move on to [Building Your First Firmware](newbs_building_firmware.md) for that. |
||||
|
@ -0,0 +1,117 @@ |
||||
# Setting up Visual Studio Code for QMK Development |
||||
|
||||
[Visual Studio Code](https://code.visualstudio.com/) (VS Code) is an open-source code editor that supports many different programming languages. |
||||
|
||||
Using a full-featured editor such as VS Code provides many advantages over a plain text editor, such as: |
||||
* intelligent code completion |
||||
* convenient navigation in the code |
||||
* refactoring tools |
||||
* build automation (no need for the command-line) |
||||
* a graphical front end for GIT |
||||
* many other tools such as debugging, code formatting, showing call hierarchies etc. |
||||
|
||||
The purpose of this page is to document how to set up VS Code for developing QMK Firmware. |
||||
|
||||
This guide covers how to configure everything needed on Windows and Ubuntu 18.04 |
||||
|
||||
# Set up VS Code |
||||
Before starting, you will want to make sure that you have all of the build tools set up, and QMK Firmware cloned. Head to the the [Newbs Getting Started Guide](newbs_getting_started.md) to get things set up, if you haven't already. |
||||
|
||||
## Windows |
||||
|
||||
### Prerequisites |
||||
|
||||
* [Git for Windows](https://git-scm.com/download/win) (This link will prompt to save/run the installer) |
||||
|
||||
1. Disable all of the options but `Git LFS (Large File Support)` and `Check daily for Git for Windows updates`. |
||||
2. Set the default editor to `Use Visual Studio Code as Git's default editor` |
||||
3. Select the `Use Git from Git Bash only` option, since that's the option that you should use here. |
||||
4. For the `Choosing HTTPS transport backend`, either option should be fine. |
||||
5. Select the `Checkout as-is, commit Unix-style line endings` option. QMK Firmware uses Unix style commits. |
||||
6. For the extra options, leave the default options as is. |
||||
|
||||
This software is needed for Git support in VS Code. It may be possible to not include this, but it is much simpler to just use this. |
||||
|
||||
* [Git Credential Manager for Windows](https://github.com/Microsoft/Git-Credential-Manager-for-Windows/releases) (Optional) |
||||
|
||||
This software provides better support for Git by providing secure storage for git credentials, MFA and personal access token generation. |
||||
|
||||
This isn't strictly needed, but we would recommend it. |
||||
|
||||
|
||||
### Installing VS Code |
||||
|
||||
1. Head to [VS Code](https://code.visualstudio.com/) and download the installer |
||||
2. Run the installer |
||||
|
||||
This part is super simple. However, there is some configuration that we need to do to ensure things are configured correctly. |
||||
|
||||
### Configuring VS Code |
||||
|
||||
First, we need to set up IntelliSense. This isn't strictly required, but it will make your life a LOT easier. To do this, we need to create the `.vscode/c_cpp_properies.json` file in the QMK Firmware folder, You can do this all manually, but I've done most of the work already. |
||||
|
||||
Grab [this file](https://gist.github.com/drashna/48e2c49ce877be592a1650f91f8473e8) and save it. You may need to edit this file, if you didn't install MSYS2 to the default location, or are using WSL/LxSS. |
||||
|
||||
Once you have saved this file, you will need to reload VS Code, if it was already running. |
||||
|
||||
?> You should see an `extensions.json` and `settings.json` file in the `.vscode` folder, as well. |
||||
|
||||
|
||||
Now, we will set up the MSYS2 window to show up in VSCode as the integrated terminal. This has a number of advantages. Mostly, you can control+click on errors and jump to those files. This makes debugging much easier. It's also nice, in that you don't have to jump to another window. |
||||
|
||||
1. Click <kbd><kbd>File</kbd> > <kbd>Preferences ></kbd> > <kbd>Settings</kbd> </kbd> |
||||
2. Click on the <kbd>{}</kbd> button, in the top right to open the `settings.json` file. |
||||
3. Set the file's content to: |
||||
|
||||
```json |
||||
{ |
||||
"terminal.integrated.shell.windows": "C:\\msys64\\usr\\bin\\bash.exe", |
||||
"terminal.integrated.env.windows": { |
||||
"MSYSTEM": "MINGW64", |
||||
"CHERE_INVOKING": "1" |
||||
}, |
||||
"terminal.integrated.shellArgs.windows": [ |
||||
"--login" |
||||
], |
||||
"terminal.integrated.cursorStyle": "line" |
||||
} |
||||
``` |
||||
|
||||
If there are settings here already, then just add everything between the first and last curly brackets. |
||||
|
||||
?> If you installed MSYS2 to a different folder, then you'll need to change the path for `terminal.integrated.shell.windows` to the correct path for your system. |
||||
|
||||
4. Hit Ctrl-` (grave) to bring up the terminal. |
||||
|
||||
This should start the terminal in the workspace's folder (so the `qmk_firmware` folder), and then you can compile your keyboard. |
||||
|
||||
|
||||
## Every other Operating System |
||||
|
||||
1. Head to [VS Code](https://code.visualstudio.com/) and download the installer |
||||
2. Run the installer |
||||
3. That's it |
||||
|
||||
No, really, that's it. The paths needed are already included when installing the packages, and it is much better about detecting the current workspace files and parsing them for IntelliSense. |
||||
|
||||
## Plugins |
||||
|
||||
There are a number of extensions that you may want to install: |
||||
|
||||
* [Git Extension Pack](https://marketplace.visualstudio.com/items?itemName=donjayamanne.git-extension-pack) - |
||||
This installs a bunch of Git related tools that may make using Git with QMK Firmware easier. |
||||
* [EditorConfig for VS Code](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) - _[Optional]_ - Helps to keep the code to the QMK Coding Conventions. |
||||
* [Bracket Pair Colorizer 2](https://marketplace.visualstudio.com/items?itemName=CoenraadS.bracket-pair-colorizer-2) - _[Optional]_ - This color codes the brackets in your code, to make it easier to reference nested code. |
||||
* [Github Markdown Preview](https://marketplace.visualstudio.com/items?itemName=bierner.github-markdown-preview) - _[Optional]_ - Makes the markdown preview in VS Code more like GitHub's. |
||||
* [VS Live Share Extension Pack](https://marketplace.visualstudio.com/items?itemName=MS-vsliveshare.vsliveshare-pack) - _[Optional]_ - This extension allows somebody else to access your workspace (or you to access somebody else's workspace) and help out. This is great if you're having issues and need some help from somebody. |
||||
* [VIM Keymap](https://marketplace.visualstudio.com/items?itemName=GiuseppeCesarano.vim-keymap) - _[Optional]_ - For those that prefer VIM style keybindings. There are other options for this, too. |
||||
* [Travis CI Status](https://marketplace.visualstudio.com/items?itemName=felixrieseberg.vsc-travis-ci-status) - _[Optional]_ - This shows the current Travis CI status, if you have it set up. |
||||
|
||||
Restart once you've installed any extensions |
||||
|
||||
# Configure VS Code for QMK |
||||
1. Click <kbd><kbd>File</kbd> > <kbd>Open Folder</kbd></kbd> |
||||
2. Open the QMK Firmware folder that you cloned from GitHub. |
||||
3. Click <kbd><kbd>File</kbd> > <kbd>Save Workspace As...</kbd></kbd> |
||||
|
||||
And now you're ready to code QMK Firmware in VS Code |
@ -0,0 +1,21 @@ |
||||
# Converting a board to use the Proton C |
||||
|
||||
If a board currently supported in QMK uses a Pro Micro (or compatible board) and you want to use the Proton C, you can generate the firmware by appending `CONVERT_TO_PROTON_C=yes` (or `CTPC=yes`) to your make argument, like this: |
||||
|
||||
make 40percentclub/mf68:default CTPC=yes |
||||
|
||||
You can add the same argument to your keymap's `rules.mk`, which will accomplish the same thing. |
||||
|
||||
This exposes the `CONVERT_TO_PROTON_C` flag that you can use in your code with `#ifdef`s, like this: |
||||
|
||||
#ifdef CONVERT_TO_PROTON_C |
||||
// Proton C code |
||||
#else |
||||
// Pro Micro code |
||||
#endif |
||||
|
||||
Before being able to compile, you may get some errors about `PORTB/DDRB`, etc not being defined, so you'll need to convert the keyboard's code to use the [GPIO Controls](internals_gpio_control.md) that will work for both ARM and AVR. This shouldn't affect the AVR builds at all. |
||||
|
||||
The Proton C only has one on-board LED (C13), and by default, the TXLED (D5) is mapped to it. If you want the RXLED (B0) mapped to it instead, add this like to your `config.h`: |
||||
|
||||
#define CONVERT_TO_PROTON_C_RXLED |
@ -0,0 +1,45 @@ |
||||
# Python Development in QMK |
||||
|
||||
This document gives an overview of how QMK has structured its python code. You should read this before working on any of the python code. |
||||
|
||||
## Script directories |
||||
|
||||
There are two places scripts live in QMK: `qmk_firmware/bin` and `qmk_firmware/util`. You should use `bin` for any python scripts that utilize the `qmk` wrapper. Scripts that are standalone and not run very often live in `util`. |
||||
|
||||
We discourage putting anything into `bin` that does not utilize the `qmk` wrapper. If you think you have a good reason for doing so please talk to us about your use case. |
||||
|
||||
## Python Modules |
||||
|
||||
Most of the QMK python modules can be found in `qmk_firmware/lib/python`. This is the path that we append to `sys.path`. |
||||
|
||||
We have a module hierarchy under that path: |
||||
|
||||
* `qmk_firmware/lib/python` |
||||
* `milc.py` - The CLI library we use. Will be pulled out into its own module in the future. |
||||
* `qmk` - Code associated with QMK |
||||
* `cli` - Modules that will be imported for CLI commands. |
||||
* `errors.py` - Errors that can be raised within QMK apps |
||||
* `keymap.py` - Functions for working with keymaps |
||||
|
||||
## CLI Scripts |
||||
|
||||
We have a CLI wrapper that you should utilize for any user facing scripts. We think it's pretty easy to use and it gives you a lot of nice things for free. |
||||
|
||||
To use the wrapper simply place a module into `qmk_firmware/lib/python/qmk/cli`, and create a symlink to `bin/qmk` named after your module. Dashes in command names will be converted into dots so you can use hierarchy to manage commands. |
||||
|
||||
When `qmk` is run it checks to see how it was invoked. If it was invoked as `qmk` the module name is take from `sys.argv[1]`. If it was invoked as `qmk-<module-name>` then everything after the first dash is taken as the module name. Dashes and underscores are converted to dots, and then `qmk.cli` is prepended before the module is imported. |
||||
|
||||
The module uses `@cli.entrypoint()` and `@cli.argument()` decorators to define an entrypoint, which is where execution starts. |
||||
|
||||
## Example CLI Script |
||||
|
||||
We have provided a QMK Hello World script you can use as an example. To run it simply run `qmk hello` or `qmk-hello`. The source code is listed below. |
||||
|
||||
``` |
||||
from milc import cli |
||||
|
||||
@cli.argument('-n', '--name', default='World', help='Name to greet.') |
||||
@cli.entrypoint('QMK Python Hello World.') |
||||
def main(cli): |
||||
cli.echo('Hello, %s!', cli.config.general.name) |
||||
``` |
@ -0,0 +1,195 @@ |
||||
# Supporting Your Keyboard in QMK Configurator |
||||
|
||||
This page covers how to properly support keyboards in the [QMK Configurator](https://config.qmk.fm/). |
||||
|
||||
|
||||
## How the Configurator Understands Keyboards |
||||
|
||||
To understand how the Configurator understands keyboards, first one must understand layout macros. For this exercise, we're going to imagine a 17-key numpad PCB, which we're going to call `numpad`. |
||||
|
||||
``` |
||||
┌───┬───┬───┬───┐ |
||||
│NLk│ / │ * │ - │ |
||||
├───┼───┼───┼───┤ |
||||
│7 │8 │9 │ + │ |
||||
├───┼───┼───┤ │ |
||||
│4 │5 │6 │ │ |
||||
├───┼───┼───┼───┤ |
||||
│1 │2 │3 │Ent│ |
||||
├───┴───┼───┤ │ |
||||
│0 │ . │ │ |
||||
└───────┴───┴───┘ |
||||
``` |
||||
|
||||
?> For more on layout macros, see [Understanding QMK: Matrix Scanning](understanding_qmk.md?id=matrix-scanning) and [Understanding QMK: Matrix to Physical Layout Map](understanding_qmk.md?id=matrix-to-physical-layout-map). |
||||
|
||||
The Configurator's API reads the keyboard's `.h` file from `qmk_firmware/keyboards/<keyboard>/<keyboard>.h`. For our numpad, this file would be `qmk_firmware/keyboards/numpad/numpad.h`: |
||||
|
||||
```c |
||||
#pragma once |
||||
|
||||
#define LAYOUT( \ |
||||
k00, k01, k02, k03, \ |
||||
k10, k11, k12, k13, \ |
||||
k20, k21, k22, \ |
||||
k30, k31, k32, k33, \ |
||||
k40, k42 \ |
||||
) { \ |
||||
{ k00, k01, k02, k03 }, \ |
||||
{ k10, k11, k12, k13 }, \ |
||||
{ k20, k21, k22, KC_NO }, \ |
||||
{ k30, k31, k32, k33 }, \ |
||||
{ k40, KC_NO, k42, KC_NO } \ |
||||
} |
||||
``` |
||||
|
||||
QMK uses `KC_NO` to designate places in the switch matrix where there is no switch. Sometimes, `XXX`, `___` or `____` are used as shorthand to make this section easier to read if it needs to be debugged. This is usually defined near the beginning of the `.h` file: |
||||
|
||||
```c |
||||
#pragma once |
||||
|
||||
#define XXX KC_NO |
||||
|
||||
#define LAYOUT( \ |
||||
k00, k01, k02, k03, \ |
||||
k10, k11, k12, k13, \ |
||||
k20, k21, k22, \ |
||||
k30, k31, k32, k33, \ |
||||
k40, k42 \ |
||||
) { \ |
||||
{ k00, k01, k02, k03 }, \ |
||||
{ k10, k11, k12, k13 }, \ |
||||
{ k20, k21, k22, XXX }, \ |
||||
{ k30, k31, k32, k33 }, \ |
||||
{ k40, XXX, k42, XXX } \ |
||||
} |
||||
``` |
||||
|
||||
!> This usage differs from that of keymap macros, which almost always use `XXXXXXX` (seven capital X's) for `KC_NO` and `_______` (seven underscores) for `KC_TRNS`. |
||||
|
||||
!> To prevent user confusion, using `KC_NO` is preferred. |
||||
|
||||
The layout macro tells the Configurator that our keyboard has 17 keys, arranged in five rows of four columns each. Our switch positions are named `k<row><column>`, counting from 0. The names themselves actually don't matter, as long as they match between the top section, which receives the keycodes from the keymap, and the bottom half which designates where each key is in the matrix. |
||||
|
||||
To display our keyboard in a way that resembles the physical keyboard, we need to build a JSON file that tells the Configurator how to tie the physical locations and sizes of our keys to our switch matrix. |
||||
|
||||
## Building the JSON file |
||||
|
||||
To build the JSON file, the easiest way is to build the layout in [Keyboard Layout Editor](http://www.keyboard-layout-editor.com/) ("KLE"), from which we'll feed the Raw Data into a QMK tool that converts this data into a JSON the Configurator will read and use. Since KLE opens by default with a numpad layout, we're just going to remove the Getting Started instructions, and use what's left. |
||||
|
||||
Once the layout is as desired, move to the Raw Data tab in KLE, and copy the contents: |
||||
|
||||
``` |
||||
["Num Lock","/","*","-"], |
||||
["7\nHome","8\n↑","9\nPgUp",{h:2},"+"], |
||||
["4\n←","5","6\n→"], |
||||
["1\nEnd","2\n↓","3\nPgDn",{h:2},"Enter"], |
||||
[{w:2},"0\nIns",".\nDel"] |
||||
``` |
||||
|
||||
To convert this data into our JSON, go to the [QMK KLE-JSON Converter](https://qmk.fm/converter/), paste the Raw Data into the Input field, and click the Convert button. After a moment, our JSON data will appear in the Output field. Copy the contents to a new text document, and name the document `info.json`, saving it in the same folder that contains `numpad.h`. |
||||
|
||||
Use the `keyboard_name` object to set the name of the keyboard. For instruction purposes, we will put each key's object on its own line. This is only to make the file more human-readable, and does not affect the Configurator's functionality. |
||||
|
||||
```json |
||||
{ |
||||
"keyboard_name": "Numpad", |
||||
"url": "", |
||||
"maintainer": "qmk", |
||||
"tags": { |
||||
"form_factor": "numpad" |
||||
}, |
||||
"width": 4, |
||||
"height": 5, |
||||
"layouts": { |
||||
"LAYOUT": { |
||||
"layout": [ |
||||
{"label":"Num Lock", "x":0, "y":0}, |
||||
{"label":"/", "x":1, "y":0}, |
||||
{"label":"*", "x":2, "y":0}, |
||||
{"label":"-", "x":3, "y":0}, |
||||
{"label":"7", "x":0, "y":1}, |
||||
{"label":"8", "x":1, "y":1}, |
||||
{"label":"9", "x":2, "y":1}, |
||||
{"label":"+", "x":3, "y":1, "h":2}, |
||||
{"label":"4", "x":0, "y":2}, |
||||
{"label":"5", "x":1, "y":2}, |
||||
{"label":"6", "x":2, "y":2}, |
||||
{"label":"1", "x":0, "y":3}, |
||||
{"label":"2", "x":1, "y":3}, |
||||
{"label":"3", "x":2, "y":3}, |
||||
{"label":"Enter", "x":3, "y":3, "h":2}, |
||||
{"label":"0", "x":0, "y":4, "w":2}, |
||||
{"label":".", "x":2, "y":4} |
||||
] |
||||
} |
||||
} |
||||
} |
||||
``` |
||||
|
||||
The `layouts` object contains the data that represents the physical layout of the keyboard. It has an object `LAYOUT`, which needs to match the name of our layout macro from `numpad.h`. The `LAYOUT` object itself has an object named `layout`, which contains one JSON object for each physical key on our keyboard, formatted as follows: |
||||
|
||||
``` |
||||
┌ The name of the key. Not displayed in the Configurator. |
||||
│ ┌ The key's X-axis location, in key units from the |
||||
│ │ keyboard's left edge. |
||||
│ │ ┌ The key's Y-axis location, in key units from |
||||
│ │ │ the keyboard's top (rear-facing) edge. |
||||
↓ ↓ ↓ |
||||
{"label":"Num Lock", "x":0, "y":0}, |
||||
``` |
||||
|
||||
Some objects will also have `"w"` and `"h"` keys, which represent a key's width and height, respectively. |
||||
|
||||
?> For more on the `info.json` files, see [QMK Keyboard Guidelines: Keyboard Metadata](hardware_keyboard_guidelines.md?id=keyboard-metadata) |
||||
|
||||
|
||||
## How the Configurator Programs Keys |
||||
|
||||
The Configurator's API uses the layout macro and the JSON file we've given it to create a visual representation of the keyboard that has each visual object tied to a specific key, in sequence: |
||||
|
||||
key in layout macro | JSON object used |
||||
:---: | :---- |
||||
k00 | {"label":"Num Lock", "x":0, "y":0} |
||||
k01 | {"label":"/", "x":1, "y":0} |
||||
k02 | {"label":"*", "x":2, "y":0} |
||||
k03 | {"label":"-", "x":3, "y":0} |
||||
k10 | {"label":"7", "x":0, "y":1} |
||||
k11 | {"label":"8", "x":1, "y":1} |
||||
k12 | {"label":"9", "x":2, "y":1} |
||||
k13 | {"label":"+", "x":3, "y":1, "h":2} |
||||
k20 | {"label":"4", "x":0, "y":2} |
||||
k21 | {"label":"5", "x":1, "y":2} |
||||
k22 | {"label":"6", "x":2, "y":2} |
||||
k30 | {"label":"1", "x":0, "y":3} |
||||
k31 | {"label":"2", "x":1, "y":3} |
||||
k32 | {"label":"3", "x":2, "y":3} |
||||
k33 | {"label":"Enter", "x":3, "y":3, "h":2} |
||||
k40 | {"label":"0", "x":0, "y":4, "w":2} |
||||
k42 | {"label":".", "x":2, "y":4} |
||||
|
||||
When a user selects the top-left key in the Configurator, and assigns Num Lock to it, the Configurator builds a keymap file with `KC_NLCK` as the first key, and so on as the keymap is built. The `label` keys are not used; they are only for the user's reference in identifying specific keys when debugging the `info.json` file. |
||||
|
||||
|
||||
## Issues and Hazards |
||||
|
||||
Currently, the Configurator does not support key rotation or non-rectangular key shapes like ISO Enter. Additionally, keys that are vertically-offset from their "row" — the arrow keys on 1800-layouts like the [TKC1800](https://github.com/qmk/qmk_firmware/tree/4ac48a61a66206beaf2fdd5f2939d8bbedd0004c/keyboards/tkc1800/) being a prominent example — confuse the KLE-to-JSON Converter, if not adjusted for by the contributor of the `info.json` file. |
||||
|
||||
### Workarounds |
||||
|
||||
#### Non-rectangular keys |
||||
|
||||
For ISO Enter keys, QMK custom is to display it as a rectangular key, 1.25u wide and 2u high, aligned so its right edge is aligned with the right edge of the alphanumeric key block. |
||||
|
||||
 |
||||
*A 60% keyboard in standard ISO layout, as rendered by QMK Configurator.* |
||||
|
||||
#### Vertically-offset keys |
||||
|
||||
For vertically-offset keys, place them in KLE as if they were not offset, then edit the Y-values as needed in the converted JSON file |
||||
|
||||
 |
||||
*An 1800-layout keyboard as rendered in Keyboard Layout Editor, without the vertical offset applied to the arrow keys.* |
||||
|
||||
 |
||||
*A Unix diff file, showing the changes needed to vertically-offset the arrow keys in our keyboard's JSON file.* |
@ -0,0 +1,73 @@ |
||||
# `info.json` |
||||
|
||||
This file is used by the [QMK API](https://github.com/qmk/qmk_api). It contains the information [QMK Configurator](https://config.qmk.fm/) needs to display a representation of your keyboard. You can also set metadata here. |
||||
|
||||
You can create `info.json` files at every level under `qmk_firmware/keyboards/<name>` to specify this metadata. These files are combined, with more specific files overriding keys in less specific files. This means you do not need to duplicate your metadata information. For example, `qmk_firmware/keyboards/clueboard/info.json` specifies `manufacturer` and `maintainer`, while `qmk_firmware/keyboards/clueboard/66/info.json` specifies more specific information about Clueboard 66%. |
||||
|
||||
## `info.json` Format |
||||
|
||||
The `info.json` file is a JSON formatted dictionary with the following keys available to be set. You do not have to set all of them, merely the keys that apply to your keyboard. |
||||
|
||||
* `keyboard_name` |
||||
* A free-form text string describing the keyboard. |
||||
* Example: `Clueboard 66%` |
||||
* `url` |
||||
* A URL to the keyboard's product page, [QMK.fm/keyboards](https://qmk.fm/keyboards) page, or other page describing information about the keyboard. |
||||
* `maintainer` |
||||
* GitHub username of the maintainer, or `qmk` for community maintained boards |
||||
* `width` |
||||
* Width of the board in Key Units |
||||
* `height` |
||||
* Height of the board in Key Units |
||||
* `layouts` |
||||
* Physical Layout representations. See the next section for more detail. |
||||
|
||||
### Layout Format |
||||
|
||||
Within our `info.json` file the `layouts` portion of the dictionary contains several nested dictionaries. The outer layer consists of QMK layout macros, for example `LAYOUT_ansi` or `LAYOUT_iso`. Within each layout macro are keys for `width`, `height`, and `key_count`, each of which should be self-explanatory. |
||||
|
||||
* `width` |
||||
* Optional: The width of the layout in Key Units |
||||
* `height` |
||||
* Optional: The height of the layout in Key Units |
||||
* `key_count` |
||||
* **Required**: The number of keys in this layout |
||||
* `layout` |
||||
* A list of Key Dictionaries describing the physical layout. See the next section for more details. |
||||
|
||||
### Key Dictionary Format |
||||
|
||||
Each Key Dictionary in a layout describes the physical properties of a key. If you are familiar with the Raw Code for <http://keyboard-layout-editor.com> you will find many of the concepts the same. We re-use the same key names and layout choices wherever possible, but unlike keyboard-layout-editor each key is stateless, inheriting no properties from the keys that came before it. |
||||
|
||||
All key positions and rotations are specified in relation to the top-left corner of the keyboard, and the top-left corner of each key. |
||||
|
||||
* `x` |
||||
* **Required**: The absolute position of the key in the horizontal axis, in Key Units. |
||||
* `y` |
||||
* **Required**: The absolute position of the key in the vertical axis, in Key Units. |
||||
* `w` |
||||
* The width of the key, in Key Units. Ignored if `ks` is provided. Default: `1` |
||||
* `h` |
||||
* The height of the key, in Key Units. Ignored if `ks` is provided. Default: `1` |
||||
* `r` |
||||
* How many degrees clockwise to rotate the key. |
||||
* `rx` |
||||
* The absolute position of the point to rotate the key around in the horizontal axis. Default: `x` |
||||
* `ry` |
||||
* The absolute position of the point to rotate the key around in the vertical axis. Default: `y` |
||||
* `ks` |
||||
* Key Shape: define a polygon by providing a list of points, in Key Units. |
||||
* **Important**: These are relative to the top-left of the key, not absolute. |
||||
* Example ISO Enter: `[ [0,0], [1.5,0], [1.5,2], [0.25,2], [0.25,1], [0,1], [0,0] ]` |
||||
* `label` |
||||
* What to name this position in the matrix. |
||||
* This should usually be the same name as what is silkscreened on the PCB at this location. |
||||
|
||||
## How is the Metadata Exposed? |
||||
|
||||
This metadata is primarily used in two ways: |
||||
|
||||
* To allow web-based configurators to dynamically generate UI |
||||
* To support the new `make keyboard:keymap:qmk` target, which bundles this metadata up with the firmware to allow QMK Toolbox to be smarter. |
||||
|
||||
Configurator authors can see the [QMK Compiler](https://docs.api.qmk.fm/using-the-api) docs for more information on using the JSON API. |
@ -0,0 +1,43 @@ |
||||
# Support |
||||
|
||||
If you need help with something, the best place to get quick support is going to be on our [Discord Server](https://discord.gg/Uq7gcHh). There is usually somebody online, and there are a bunch of very helpful people there. |
||||
|
||||
Don't forget to read our [Code of Conduct](https://qmk.fm/coc/). |
||||
|
||||
## Help! I don't know where to start! |
||||
|
||||
If this is the case, then you should start with our [Newbs Guide](https://docs.qmk.fm/#/newbs). There is a lot of great info there, and that should cover everything you need to get started. |
||||
|
||||
If that's an issue, hop onto the [QMK Configurator](https://config.qmk.fm), as that will handle a majority of what you need there. |
||||
|
||||
## Help! I'm having issues flashing! |
||||
|
||||
First, head to the [Compiling/Flashing FAQ Page](https://docs.qmk.fm/#/faq_build). There is a good deal of info there, and you'll find a bunch of solutions to common issues there. |
||||
|
||||
## Help, I have an issue that isn't covered by the links above |
||||
|
||||
Okay, that's fine. Then please check the [open issues in our GitHub](https://github.com/qmk/qmk_firmware/issues) to see if somebody is experiencing the same thing (make sure it's not just similar, but actually the same). |
||||
|
||||
If you can't find anything, then please open a [new issue](https://github.com/qmk/qmk_firmware/issues/new)! |
||||
|
||||
## What if I found a bug? |
||||
|
||||
Then please open an [issue](https://github.com/qmk/qmk_firmware/issues/new), and if you know how to fix it, open up a Pull Request on GitHub with the fix. |
||||
|
||||
## But `git` and `GitHub` are intimidating! |
||||
|
||||
Don't worry, we have some pretty nice [Guidelines](https://docs.qmk.fm/#/newbs_best_practices) on how to start using `git` and GitHub to make things easier to develop. |
||||
|
||||
Additionally, you can find additional `git` and GitHub related links [here](https://docs.qmk.fm/#/newbs_learn_more_resources). |
||||
|
||||
## I have a Keyboard that I want to add support for |
||||
|
||||
Awesome! Open up a Pull Request for it. We'll review the code, and merge it! |
||||
|
||||
### What if I want to do brand it with `QMK`? |
||||
|
||||
That's amazing! We would love to assist you with that! |
||||
|
||||
In fact, we have a [whole page](https://qmk.fm/powered/) dedicated to adding QMK Branding to your page and keyboard. This covers pretty much everything you need (knowledge and images) to officially support QMK. |
||||
|
||||
If you have any questions about this, open an issue or head to [Discord](https://discord.gg/Uq7gcHh). |
@ -0,0 +1,32 @@ |
||||
# QMK机械键盘固件 |
||||
|
||||
[](https://github.com/qmk/qmk_firmware/tags) |
||||
[](https://travis-ci.org/qmk/qmk_firmware) |
||||
[](https://discord.gg/Uq7gcHh) |
||||
[](https://docs.qmk.fm) |
||||
[](https://github.com/qmk/qmk_firmware/pulse/monthly) |
||||
[](https://github.com/qmk/qmk_firmware/) |
||||
|
||||
## 什么是 QMK 固件? |
||||
|
||||
QMK (*Quantum Mechanical Keyboard*) 是一个社区维护的开源软件,包括 QMK 固件, QMK 工具箱, qmk.fm网站, 和这些文档。QMK 固件是一个基于[tmk\_keyboard](http://github.com/tmk/tmk_keyboard)的键盘固件,它在爱特梅尔AVR微控制器实现一些有用的功能,确切地说, 是在 [OLKB product line](http://olkb.com), 在 [ErgoDox EZ](http://www.ergodox-ez.com) 键盘, 和 [Clueboard product line](http://clueboard.co/). 上。它被移植到使用ChibiOS的ARM芯片上. 它可以在飞线键盘或定制PCB键盘中发挥功能. |
||||
|
||||
## 如何得到它 |
||||
|
||||
如果你打算贡献布局, 键盘, 或者其他QMK特性, 一下是最简单的方法:[从Github获得repo分支](https://github.com/qmk/qmk_firmware#fork-destination-box), 并克隆你的repo到本地进行编辑,推送,然后从你的分支打开 [Pull Request](https://github.com/qmk/qmk_firmware/pulls). |
||||
|
||||
此外, 你也可以直接下载 ([zip](https://github.com/qmk/qmk_firmware/zipball/master), [tar](https://github.com/qmk/qmk_firmware/tarball/master)), 或者从git克隆 (`git@github.com:qmk/qmk_firmware.git`), 或 https (`https://github.com/qmk/qmk_firmware.git`). |
||||
|
||||
## 如何编译 |
||||
|
||||
在你能编译之前, 你需要[部署环境](getting_started_build_tools.md) 用于 AVR or/and ARM 开发。完成后, 你可以使用 `make` 命令来编译一个键盘和布局使用以下命令: |
||||
|
||||
make planck/rev4:default |
||||
|
||||
这将建立 `planck`的`rev4` 修订版本并使用 `default`布局。并非所有键盘都有修订版本 (也叫做子项目或文件夹),在此情况下,修订版本可以省略,如下: |
||||
|
||||
make preonic:default |
||||
|
||||
## 如何定制 |
||||
|
||||
QMK 有许多 [特性](features.md)来探索,也有很多 [参考文档](http://docs.qmk.fm) 供您发掘。你可以通过修改 [布局](keymap.md)和[键码](keycodes.md)来利用许多特性。 |
@ -0,0 +1,106 @@ |
||||
* [完全菜鸟指南](newbs.md) |
||||
* [入门](newbs_getting_started.md) |
||||
* [构建你的第一个固件](newbs_building_firmware.md) |
||||
* [刷新固件](newbs_flashing.md) |
||||
* [测试和调试](newbs_testing_debugging.md) |
||||
* [Git最佳实践](newbs_best_practices.md) |
||||
* [学习资源](newbs_learn_more_resources.md) |
||||
|
||||
* [QMK基础](README.md) |
||||
* [QMK简介](getting_started_introduction.md) |
||||
* [向QMK贡献](contributing.md) |
||||
* [如何使用Github](getting_started_github.md) |
||||
* [获得帮助](getting_started_getting_help.md) |
||||
|
||||
* [问题解答](faq.md) |
||||
* [一般问题](faq_general.md) |
||||
* [构建/编译](faq_build.md) |
||||
* [调试/故障排除](faq_debug.md) |
||||
* [键盘映射](faq_keymap.md) |
||||
|
||||
* 详细指南 |
||||
* [安装构建工具](getting_started_build_tools.md) |
||||
* [vagrant指南](getting_started_vagrant.md) |
||||
* [构建/编译指令](getting_started_make_guide.md) |
||||
* [刷新固件](flashing.md) |
||||
* [定制功能](custom_quantum_functions.md) |
||||
* [映射概述](keymap.md) |
||||
|
||||
* [硬件](hardware.md) |
||||
* [AVR处理器](hardware_avr.md) |
||||
* [驱动](hardware_drivers.md) |
||||
|
||||
* 参考 |
||||
* [键盘指南](hardware_keyboard_guidelines.md) |
||||
* [配置选项](config_options.md) |
||||
* [键码](keycodes.md) |
||||
* [记录最佳实践](documentation_best_practices.md) |
||||
* [文档模板](documentation_templates.md) |
||||
* [术语表](reference_glossary.md) |
||||
* [单元测试](unit_testing.md) |
||||
* [有用的功能](ref_functions.md) |
||||
* [配置器支持](reference_configurator_support.md) |
||||
* [info.json 格式](reference_info_json.md) |
||||
|
||||
* [特性](features.md) |
||||
* [基本键码](keycodes_basic.md) |
||||
* [US ANSI控制码](keycodes_us_ansi_shifted.md) |
||||
* [量子键码](quantum_keycodes.md) |
||||
* [高级键码](feature_advanced_keycodes.md) |
||||
* [音频](feature_audio.md) |
||||
* [自动shift](feature_auto_shift.md) |
||||
* [背光](feature_backlight.md) |
||||
* [蓝牙](feature_bluetooth.md) |
||||
* [热改键](feature_bootmagic.md) |
||||
* [组合](feature_combo) |
||||
* [命令](feature_command.md) |
||||
* [动态宏指令](feature_dynamic_macros.md) |
||||
* [编码器](feature_encoders.md) |
||||
* [重音号Esc复合键](feature_grave_esc.md) |
||||
* [自锁键](feature_key_lock.md) |
||||
* [布局](feature_layouts.md) |
||||
* [前导键](feature_leader_key.md) |
||||
* [LED阵列](feature_led_matrix.md) |
||||
* [宏指令](feature_macros.md) |
||||
* [鼠标键](feature_mouse_keys.md) |
||||
* [一键功能](feature_advanced_keycodes.md#one-shot-keys) |
||||
* [指针设备](feature_pointing_device.md) |
||||
* [PS/2鼠标](feature_ps2_mouse.md) |
||||
* [RGB灯光](feature_rgblight.md) |
||||
* [RGB矩阵](feature_rgb_matrix.md) |
||||
* [空格候补换挡](feature_space_cadet_shift.md) |
||||
* [空格候补换挡回车](feature_space_cadet_shift_enter.md) |
||||
* [速录机](feature_stenography.md) |
||||
* [换手](feature_swap_hands.md) |
||||
* [多击键](feature_tap_dance.md) |
||||
* [终端](feature_terminal.md) |
||||
* [热敏打印机](feature_thermal_printer.md) |
||||
* [Unicode](feature_unicode.md) |
||||
* [用户空间](feature_userspace.md) |
||||
* [速度键](feature_velocikey.md) |
||||
|
||||
* 针对制造者和定制者 |
||||
* [手工连线指南](hand_wire.md) |
||||
* [ISP刷新指南](isp_flashing_guide.md) |
||||
* [ARM调试指南](arm_debugging.md) |
||||
* [I2C驱动](i2c_driver.md) |
||||
* [GPIO控制器](internals_gpio_control.md) |
||||
* [Proton C转换](proton_c_conversion.md) |
||||
|
||||
* 深入了解 |
||||
* [键盘如何工作](how_keyboards_work.md) |
||||
* [理解QMK](understanding_qmk.md) |
||||
|
||||
* 其他话题 |
||||
* [使用Eclipse开发QMK](other_eclipse.md) |
||||
* [使用VSCode开发QMK](other_vscode.md) |
||||
* [支持](support.md) |
||||
|
||||
* QMK 内构 (正在编写) |
||||
* [定义](internals_defines.md) |
||||
* [输入回调寄存器](internals_input_callback_reg.md) |
||||
* [Midi设备](internals_midi_device.md) |
||||
* [Midi设备设置过程](internals_midi_device_setup_process.md) |
||||
* [Midi工具库](internals_midi_util.md) |
||||
* [发送函数](internals_send_functions.md) |
||||
* [Sysex工具](internals_sysex_tools.md) |
@ -0,0 +1,205 @@ |
||||
# 如何做贡献 |
||||
|
||||
👍🎉 首先感谢各位百忙之中抽空阅读本文档,并为我们无私奉献。给您点赞啦! 🎉👍 |
||||
|
||||
第三方的帮助让Q酱成长了许多呢,Q酱也从你们那学到了不少新东西。Q酱希望每一个想帮助我的人都能很方便的做出有用的贡献。在这里我给摩拳擦掌的你们写了一点引导,让你们的代码在不对我做重大改动的情况下都能成功的被采纳哦。 |
||||
|
||||
* [项目概况](#项目概况) |
||||
* [代码规范](#代码规范) |
||||
* [一般教程](#一般教程) |
||||
* [行为守则对于我来说有何意义?](#行为守则对于我来说有何意义?) |
||||
|
||||
## 这文章巨长无比不想读啊! 我就想问个问题而已! |
||||
|
||||
您要是想问关于Q酱的问题的话可以在[OLKB Subreddit](https://reddit.com/r/olkb)或者是[Discord](https://discord.gg/Uq7gcHh)随意问。 |
||||
|
||||
请记住: |
||||
|
||||
* 维护Q酱的小可爱有的时候可能会有点忙,不能及时回答您的问题,耐心等等,他们都是很nice的人呀。 |
||||
* 维护Q酱的人都是很无私的善良的人。无论是贡献代码还是回答问题,都是义务的。有时见到他们努力回答各种问题,解决各种BUG,Q酱也是很心疼的。 |
||||
* 您可以看看下面的教程,可以让您的问题浅显易懂,更容易回答: |
||||
* https://opensource.com/life/16/10/how-ask-technical-questions |
||||
* http://www.catb.org/esr/faqs/smart-questions.html |
||||
|
||||
# 项目概况 |
||||
|
||||
Q酱很大一部分是用C语言组成的,不过有一小部分特性是C++的。怎么说呢,都是我的一部分,两个我都爱。Q酱一般是在键盘上的嵌入式处理器那里工作的,尤其与AVR([LUFA](http://www.fourwalledcubicle.com/LUFA.php))和ARM ([ChibiOS](http://www.chibios.com))两小哥哥搭配,干活不累,嘻嘻。如果您精通Arduino的话您会发现很多熟悉的概念,但也有点不爽,因为您以前的经验可能没法用来帮助Q酱。 |
||||
|
||||
<!-- 需要修正: 这里放些学习C语言的资源。另外感谢修正的小可爱。谢谢您了。--> |
||||
|
||||
# Q酱,我在哪能帮助你嘞? |
||||
|
||||
您要是有问题的话可以 [提出一个issue](https://github.com/qmk/qmk_firmware/issues) 或 [在Discord上交流一下](https://discord.gg/Uq7gcHh). |
||||
|
||||
# Q酱,我如何帮助你? |
||||
|
||||
您以前是否没为开源贡献过代码,而又想知道帮助Q酱是怎么一回事? 稍安勿躁,咱给您总结一下! |
||||
|
||||
0. 先注册一个 [GitHub](https://github.com) 账户。 |
||||
1. 做好一个你要贡献的布局,那就要 [找一个你想解决的问题](https://github.com/qmk/qmk_firmware/issues),或者 [找一个你想添加的特性](https://github.com/qmk/qmk_firmware/issues?q=is%3Aopen+is%3Aissue+label%3Afeature)。 |
||||
2. 把关联着问题的仓库分叉(fork)到你的仓库。这样你在`你的GitHub用户名/qmk_firmware`就有一个仓库备份啦。 |
||||
3. 使用 `git clone https://github.com/此处添GitHub用户名/此处添仓库名.git`这个命令把仓库同步到你的电脑中。 |
||||
4. 您要是想开发一个新特性的话可以先创建一个issue和Q酱的维护者讨论一下您要做什么。 |
||||
5. 使用`git checkout -b 此处写分支名字(别用汉字)`命令来创建一个分支(branch)用于开发。 |
||||
6. 对要解决的问题或要添加的特性进行适当的更改。 |
||||
7. 使用 `git add 把改变的文件的目录写这里` 可以添加改变的文件内容到git用于管理工程状态的索引(快照)里。 |
||||
8. 使用 `git commit -m "这里写修改的相关信息"` 来描述你做出了什么修改。 |
||||
9. 使用 `git push origin 此处写分支名字`来把你的更改同步到GitHub库里(反正不是打篮球那个库里)。 |
||||
10. 提交一个[QMK 固件的pull request](https://github.com/qmk/qmk_firmware/pull/new/master)。 |
||||
11. 给你的pull request拟一个标题,包括简短的描述和问题或错误代码。比如, 你可以起一个这样的"Added more log outputting to resolve #4352"(最好用英语,毕竟Q酱的中文也不是那么的溜,有可能会看不懂中文)。 |
||||
12. 在描述(description)里面写你做了哪些更改,你的代码里还存在什么问题, 或者你想问维护的小可爱们的问题。你的your pull request有点小问题无伤大雅(本来也没有完美的代码嘛), 维护的小可爱们会竭尽全力帮您改进的! |
||||
13. 维护人员审查代码可能需要一些时间。 |
||||
14. 维护人员会通知您要更改什么地方,然后您就按照建议改一改。 |
||||
15. 预祝您合并成功! |
||||
|
||||
# 代码规范 |
||||
|
||||
其实也没有什么特别严格的规范啦,但是俗话说的好:没有规矩,不成方圆。您可以看一下您的要改动的代码周围的画风,然后保持队形。如果你感觉周围都不知道是什么牛鬼蛇神的话就看看下面的建议: |
||||
|
||||
* 我们用肆(4)个空格来缩进(软件中也可以设置到Tab键) |
||||
* 我们使用改良的1TBS(允许单行样式) |
||||
* 左大括号: 在开放性语句块那行的末尾 |
||||
* 右大括号: 和开放性语句块第一个字母对齐 |
||||
* Else If: 将右大括号放在行的开头,下一个左大括号放在同一行的结尾 |
||||
* 可选大括号: 可选大括号是必选的 |
||||
* 应该这样: if (condition) { return false; } |
||||
* 不应该这样: if (condition) return false; |
||||
* 建议使用C语言风格的注释: `/* */` |
||||
* 把注释想象成一个描述特征的故事 |
||||
* 充分使用注释来描述你为何这样修改 |
||||
* 有些公认的东西就不要写到注释里面了 |
||||
* 如果你不知道注释是否多余,看下面 |
||||
* 一般不要主动换行,主动换行的话每行不要超过76列 |
||||
* 要把 `#pragma once` 放到头文件的开始哦,抛弃老土的(`#ifndef THIS_FILE_H`, `#define THIS_FILE_H`, ..., `#endif`)吧 |
||||
* 下面两种预处理命令都可以用: `#ifdef DEFINED` 还有 `#if defined(DEFINED)` |
||||
* 以上那句对处女座不是很友好哈,处女座的朋友们就别纠结了,直接 `#if defined(DEFINED)` 。 |
||||
* 还有就是选好一种风格就一直用,一直用一直爽,不要朝三暮四, 除非你要变化到多重条件的 `#if`。 |
||||
* `#` 和 `if`要挨在一起哦,再让本空格在中间冒充电灯泡本空格会生气的。 |
||||
* 以下是缩进规则: |
||||
* 首先考虑可读性,强迫症的朋友们总想要保持代码的高一致性,这样可不好。 |
||||
* 保证文件已有风格不变。如果代码本来就是杂糅风格,那就见机行事,让你的修改更有意义些。 |
||||
* 其实你也可以在缩进的时候看看周围其他代码,然后范水模山,预处理命令可以有自己的缩进风格。 |
||||
|
||||
可以参照下面: |
||||
|
||||
```c |
||||
/* foo 的 Enums*/ |
||||
enum foo_state { |
||||
FOO_BAR, |
||||
FOO_BAZ, |
||||
}; |
||||
|
||||
/* 有返回值的情况 */ |
||||
int foo(void) { |
||||
if (some_condition) { |
||||
return FOO_BAR; |
||||
} else { |
||||
return -1; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
# Clang-format的自动格式化 |
||||
[Clang-format](https://clang.llvm.org/docs/ClangFormat.html) 是LLVM的一部分,可以帮你自动格式化代码。我们给你准备好了一个适用于以上规范的配置文件,会帮你调整缩进和换行,你只需要写好括号就好。有了它,你再也不用担心调整代码格式太耗时,没有时间陪伴自己(虚构)的另一半了。 |
||||
|
||||
使用[LLVM 完整安装](http://llvm.org/builds/)可以在Windows上安装clang-format, Ubuntu用户要用`sudo apt install clang-format`。 |
||||
|
||||
命令行的朋友们, 加上 `-style=file`选项就会自动在QMK的根目录寻找.clang-format配置文件了。 |
||||
|
||||
VSCode用户, 标准的 C/C++ 插件就支持clang-format, 或者可以用[独立扩展](https://marketplace.visualstudio.com/items?itemName=LLVMExtensions.ClangFormat)也行。 |
||||
|
||||
有些东西(比如LAYOUT宏) 会被clang-format打乱,所以那些文件就别用clang-format了,这里就教您一个小窍门,在`// clang-format off` 和 `//clang-format on`之间装上会被搞乱的代码就好了。 |
||||
|
||||
# 一般教程 |
||||
|
||||
你可以给Q酱的不同部分添砖加瓦,但也要用不同的方法严谨检查。不论你修改哪里最好还是看看下边。 |
||||
|
||||
* 将PR(pull request)分成一个个的逻辑单元。 比如,不要一次将两个新特性PR出去。要添加的特性排好队,一个一个来。 |
||||
* 提交之前看一眼,`git diff --check`的空格一定要写对了 |
||||
* 确定你的代码能通过编译 |
||||
* 布局: 确定`make keyboard:your_new_keymap` 不返回错误 |
||||
* 键盘: 确定 `make keyboard:all` 不返回错误 |
||||
* 核心代码: 确定 `make all` 不返回错误 |
||||
* 提交的信息尽量明确。第一行写点简短介绍(每行不多于70个英文字母), 第二行空着,第三行和后面就要写些必要的细节了。最好用英文写,比如: |
||||
|
||||
``` |
||||
Adjust the fronzlebop for the kerpleplork |
||||
|
||||
The kerpleplork was intermittently failing with error code 23. The root cause was the fronzlebop setting, which causes the kerpleplork to activate every N iterations. |
||||
|
||||
Limited experimentation on the devices I have available shows that 7 is high enough to avoid confusing the kerpleplork, but I'd like to get some feedback from people with ARM devices to be sure. |
||||
``` |
||||
|
||||
## 文档 |
||||
|
||||
想帮助Q酱当然是先看文档最简单了。找到这个文档哪里错了然后改正它对于你来说超级简单! 我们也对有写文档能力的人求贤若渴,如果你是对的人[点这个](#Q酱,我在哪能帮助你嘞?)! |
||||
|
||||
文档呢,都静静的放在`qmk_firmware/docs` 目录里, 也或者您想为网页做贡献的话也是可以的哦。 |
||||
|
||||
在文档中附代码案例时, 先观察文档其他地方的命名规范。比如, 把enums的名字都改成像`my_layers`或者`my_keycodes`来防止名字不一致的enums被当作特务枪毙: |
||||
|
||||
```c |
||||
enum my_layers { |
||||
_FIRST_LAYER, |
||||
_SECOND_LAYER |
||||
}; |
||||
|
||||
enum my_keycodes { |
||||
FIRST_LAYER = SAFE_RANGE, |
||||
SECOND_LAYER |
||||
}; |
||||
``` |
||||
|
||||
## 布局 |
||||
|
||||
大多数QMK新手都从创建一个自己的布局开始。我们尽力保证布局规范宽松 (毕竟布局是个性的体现) 不过建议遵守以下准则,这样可以让别人更好理解你的代码 |
||||
|
||||
* 用 [模板](documentation_templates.md)写个`readme.md`。 |
||||
* 所有的布局PR都会被squash, 如果你想知道你的提交是怎么被squash的那你就自己来吧 |
||||
* 不要把新特性和布局一起PR。可以分别PR他们 |
||||
* 布局文件夹就不要放`Makefile`了,这个操作都过时啦 |
||||
* 更新文件头部的copyrights(看`%YOUR_NAME%`那) |
||||
|
||||
## 键盘 |
||||
|
||||
QMK的最终归宿是键盘。有些键盘是社区维护的,有一些是制作这些键盘的人维护的。`readme.md`会告诉你是谁维护了这个键盘,如果你对某个键盘有疑问,可以 [创建一个Issue](https://github.com/qmk/qmk_firmware/issues) 来问一问维护者。 |
||||
|
||||
我们建议你按下面的来操作: |
||||
|
||||
* 用[模板](documentation_templates.md)写`readme.md`。 |
||||
* 提交数量尽量合理,不然我们可就要把你的PR给squash了。 |
||||
* 不要把新特性和新键盘一起PR。可以分别PR他们 |
||||
* 用父文件夹的名字命名 `.c`/`.h`文件, 比如`/keyboards/<kb1>/<kb2>/<kb2>.[ch]` |
||||
* 键盘文件夹就不要放`Makefile`了,这个操作都过时啦 |
||||
* 更新文件头部的copyrights(看`%YOUR_NAME%`那) |
||||
|
||||
## Quantum/TMK 核心 |
||||
|
||||
在您废寝忘食地开发Q酱新特性或者帮Q酱驱虫之前,一定要确保你的工作是有意义的。看看[了解QMK](understanding_qmk.md)你会对Q酱有更深的了解,这个文档将带你领略QMK的程序流程。现在你应该和维护团对谈谈来了解实现你想法的最佳方法了。一下渠道都可以: |
||||
|
||||
* [在Discord交流](https://discord.gg/Uq7gcHh) |
||||
* [建立一个Issue](https://github.com/qmk/qmk_firmware/issues/new) |
||||
|
||||
新特性和BUG的修复影响所有键盘。开发组也在翻修QMK。所以,在实施重大返修之前一定要讨论一下。如果你在没有事先与维护团队沟通的情况下提交了一个PR,而且你的选择与维护团队的计划方向不符,那你可能要面临大改了。 |
||||
|
||||
修复BUG或者开发新特性之前看看这个: |
||||
|
||||
* **默认不启用** - QMK运行的芯片多数内存有限,所以首要考虑的还应该是布局不要被破坏,于是特性默认是不启用的。你喜欢什么特性的话就打开它,如果你觉得有些特性应该默认开启或者你能帮助缩减代码,那就联系维护组吧。 |
||||
* **提交之前在本地编译** - 这个简直就是家喻户晓了,但是也确实需要编译啊! 我们的Travis系统会发现一切问题,但是自己编译一下可要比在线等快多了。 |
||||
* **注意版本和芯片平台** - 有那么几个键盘有支持不同配置甚至是不同芯片的版本。试着写一个能AVR和ARM两个平台运行的特性,或者在不支持的平台自动禁用。 |
||||
* **解释你的新特性** - 在`docs/`写个文档, 你可以创建新文档或者写到现有文档中。如果你不把它记录下来,其他人就无法从你的努力中获益。 |
||||
|
||||
也可以看看以下建议: |
||||
|
||||
* 提交数量尽量合理,不然我们可就要把你的PR给squash了。 |
||||
* 不要把新特性、布局和键盘一起PR。可以分别PR他们。 |
||||
* 给你的特性写[单元测试](unit_testing.md)。 |
||||
* 你编辑的文件风格要一致,如果风格不明确或者是混搭风的,你就要先看看[代码规范](#代码规范)确认情况。 |
||||
|
||||
## 重构 |
||||
|
||||
为了保持QMK脉络清晰,Q酱打算深入规划重构一下自己,然后让合作者进行修改。如果你有重构的思路或建议[创建一个issue](https://github.com/qmk/qmk_firmware/issues), Q酱很乐意讨论一下怎么改进一下。 |
||||
|
||||
# 行为守则对于我来说有何意义? |
||||
|
||||
我们的[行为守则](https://github.com/qmk/qmk_firmware/blob/master/CODE_OF_CONDUCT.md) 是说明您有责任尊重和礼貌地对待项目中的每个人,无论他们的身份如何。 如果你是我们行为准则所描述的不当行为的受害者,我们将站在你这边,并按照行为准则对施暴者进行适当谴责。 |
@ -0,0 +1,490 @@ |
||||
# 如何定制你键盘的功能 |
||||
|
||||
对于很多人来说客制化键盘可不只是向你的电脑发送你按了那个件这么简单。你肯定想实现比简单按键和宏更复杂的功能。QMK有能让你注入代码的钩子, 覆盖功能, 另外,还可以自定义键盘在不同情况下的行为。 |
||||
|
||||
本页不假定任何特殊的QMK知识,但阅读[理解QMK](understanding_qmk.md)将会在更基础的层面帮你理解发生了什么。 |
||||
|
||||
## A Word on Core vs 键盘 vs 布局 |
||||
|
||||
我们把qmk组织成一个层次结构: |
||||
|
||||
* Core (`_quantum`) |
||||
* Keyboard/Revision (`_kb`) |
||||
* Keymap (`_user`) |
||||
|
||||
下面描述的每一个函数都可以在定义上加一个`_kb()`或 `_user()` 后缀。 建议在键盘/修订层使用`_kb()`后缀,在布局层使用`_user()`后缀。 |
||||
|
||||
在键盘/修订层定义函数时,`_kb()`在执行任何代码前先调用`_user()`是必要的,不然布局层函数就不要被调用。 |
||||
<!-- 翻译问题:上面那句翻译的不太好--> |
||||
# 自定义键码 |
||||
|
||||
到目前为止,最常见的任务是更改现有键码的行为或创建新的键码。从代码角度来看这些操作都很相似。 |
||||
|
||||
## 定义一个新键码 |
||||
|
||||
创建键码第一步,先枚举出它全部,也就是给键码起个名字并分配唯一数值。QMK没有直接限制最大键码值大小,而是提供了一个`SAFE_RANGE`宏。你可以在枚举时用`SAFE_RANGE`来保证你取得了唯一的键码值。 |
||||
|
||||
|
||||
这有枚举两个键码的例子。把这块加到`keymap.c`的话你就在布局中能用`FOO`和`BAR`了。 |
||||
|
||||
```c |
||||
enum my_keycodes { |
||||
FOO = SAFE_RANGE, |
||||
BAR |
||||
}; |
||||
``` |
||||
|
||||
## 为键码的行为编程 |
||||
|
||||
当你覆盖一个已存在按键的行为时,或将这个行为赋给新键时,你要用`process_record_kb()`和`process_record_user()`函数。这俩函数在键处理中真实键事件被处理前被QMK调用。如果这俩函数返回`true`,QMK将会用正常的方式处理键码。这样可以很方便的扩展键码的功能而不是替换它。如果函数返回`false` QMK会跳过正常键处理,然后发送键子抬起还是按下事件就由你决定了。 |
||||
|
||||
当某个键按下或释放时这俩函数会被调用。 |
||||
|
||||
### process_record_user()`函数示例实现 |
||||
|
||||
这个例子做了两个事。自定义了一个叫做`FOO`的键码的行为,并补充了在按下回车时播放音符。 |
||||
|
||||
```c |
||||
bool process_record_user(uint16_t keycode, keyrecord_t *record) { |
||||
switch (keycode) { |
||||
case FOO: |
||||
if (record->event.pressed) { |
||||
// 按下时做些什么 |
||||
} else { |
||||
// 释放时做些什么 |
||||
} |
||||
return false; // 跳过此键的所有进一步处理 |
||||
case KC_ENTER: |
||||
// 当按下回车时播放音符 |
||||
if (record->event.pressed) { |
||||
PLAY_NOTE_ARRAY(tone_qwerty); |
||||
} |
||||
return true; // 让QMK触发回车按下/释放事件 |
||||
default: |
||||
return true; // 正常处理其他键码 |
||||
} |
||||
} |
||||
``` |
||||
|
||||
### `process_record_*` 函数文档 |
||||
|
||||
* 键盘/修订: `bool process_record_kb(uint16_t keycode, keyrecord_t *record)` |
||||
* 布局: `bool process_record_user(uint16_t keycode, keyrecord_t *record)` |
||||
|
||||
`keycode(键码)`参数是在布局上定义的,比如`MO(1)`, `KC_L`, 等等。 你要用 `switch...case` 块来处理这些事件。 |
||||
|
||||
`record`参数含有实际按键的信息: |
||||
|
||||
```c |
||||
keyrecord_t record { |
||||
keyevent_t event { |
||||
keypos_t key { |
||||
uint8_t col |
||||
uint8_t row |
||||
} |
||||
bool pressed |
||||
uint16_t time |
||||
} |
||||
} |
||||
``` |
||||
|
||||
# LED控制 |
||||
|
||||
qmk提供了读取HID规范包含的5个LED的方法。: |
||||
|
||||
* `USB_LED_NUM_LOCK` |
||||
* `USB_LED_CAPS_LOCK` |
||||
* `USB_LED_SCROLL_LOCK` |
||||
* `USB_LED_COMPOSE` |
||||
* `USB_LED_KANA` |
||||
|
||||
这五个常量对应于主机LED状态的位置位。 |
||||
有两种方法可以获得主机LED状态: |
||||
|
||||
* 通过执行 `led_set_user()` |
||||
* 通过调用 `host_keyboard_leds()` |
||||
|
||||
## `led_set_user()` |
||||
|
||||
当5个LED中任何一个的状态需要改变时,此函数将被调用。此函数通过参数输入LED参数。 |
||||
使用`IS_LED_ON(usb_led, led_name)`和`IS_LED_OFF(usb_led, led_name)`这两个宏来检查LED状态。 |
||||
|
||||
!> `host_keyboard_leds()`可能会在`led_set_user()`被调用前返回新值。 |
||||
|
||||
### `led_set_user()`函数示例实现 |
||||
|
||||
```c |
||||
void led_set_user(uint8_t usb_led) { |
||||
if (IS_LED_ON(usb_led, USB_LED_NUM_LOCK)) { |
||||
writePinLow(B0); |
||||
} else { |
||||
writePinHigh(B0); |
||||
} |
||||
if (IS_LED_ON(usb_led, USB_LED_CAPS_LOCK)) { |
||||
writePinLow(B1); |
||||
} else { |
||||
writePinHigh(B1); |
||||
} |
||||
if (IS_LED_ON(usb_led, USB_LED_SCROLL_LOCK)) { |
||||
writePinLow(B2); |
||||
} else { |
||||
writePinHigh(B2); |
||||
} |
||||
if (IS_LED_ON(usb_led, USB_LED_COMPOSE)) { |
||||
writePinLow(B3); |
||||
} else { |
||||
writePinHigh(B3); |
||||
} |
||||
if (IS_LED_ON(usb_led, USB_LED_KANA)) { |
||||
writePinLow(B4); |
||||
} else { |
||||
writePinHigh(B4); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
### `led_set_*`函数文档 |
||||
|
||||
* 键盘/修订: `void led_set_kb(uint8_t usb_led)` |
||||
* 布局: `void led_set_user(uint8_t usb_led)` |
||||
|
||||
## `host_keyboard_leds()` |
||||
|
||||
调用这个函数会返回最后收到的LED状态。这个函数在`led_set_*`之外读取LED状态时很有用,比如在[`matrix_scan_user()`](#矩阵扫描代码). |
||||
为了便捷,你可以用`IS_HOST_LED_ON(led_name)`和`IS_HOST_LED_OFF(led_name)` 宏,而不直接调用和检查`host_keyboard_leds()`。 |
||||
|
||||
## 设置物理LED状态 |
||||
|
||||
一些键盘实现了为设置物理LED的状态提供了方便的方法。 |
||||
|
||||
### Ergodox Boards |
||||
|
||||
Ergodox实现了提供`ergodox_right_led_1`/`2`/`3_on`/`off()`来让每个LED开或关, 也可以用 `ergodox_right_led_on`/`off(uint8_t led)` 按索引打开或关闭他们。 |
||||
|
||||
此外,还可以使用`ergodox_led_all_set(uint8_t n)`指定所有LED的亮度级别;针对每个LED用`ergodox_right_led_1`/`2`/`3_set(uint8_t n)`;使用索引的话用`ergodox_right_led_set(uint8_t led, uint8_t n)`。 |
||||
|
||||
Ergodox boards 同时定义了最低亮度级别`LED_BRIGHTNESS_LO`和最高亮度级别`LED_BRIGHTNESS_HI`(默认最高). |
||||
|
||||
# 键盘初始化代码 |
||||
|
||||
键盘初始化过程有几个步骤。你是用那个函数取决于你想要做什么。 |
||||
|
||||
有三个主要初始化函数,按调用顺序列出。 |
||||
|
||||
* `keyboard_pre_init_*` - 会在大多数其他东西运行前运行。适用于哪些需要提前运行的硬件初始化。 |
||||
* `matrix_init_*` - 在固件启动过程中间被调用。此时硬件已初始化,功能尚未初始化。 |
||||
* `keyboard_post_init_*` - 在固件启动过程最后被调用。大多数情况下,你的“客制化”代码都可以放在这里。 |
||||
|
||||
!> 对于大多数人来说`keyboard_post_init_user`是你想要调用的函数。例如, 此时你可以设置RGB灯发光。 |
||||
|
||||
## 键盘预初始化代码 |
||||
|
||||
这代码极早运行,甚至都在USB初始化前运行。 |
||||
|
||||
在这之后不久矩阵就被初始化了。 |
||||
|
||||
对于大多数用户来说,这用不到,因为它主要是用于面向硬件的初始化。 |
||||
|
||||
但如果你有硬件初始化的话放在这里再好不过了(比如初始化LED引脚一类的). |
||||
|
||||
### `keyboard_pre_init_user()`函数示例实现 |
||||
|
||||
本例中在键盘级别,设定 B0, B1, B2, B3, 和 B4 是LED引脚。 |
||||
|
||||
```c |
||||
void keyboard_pre_init_user(void) { |
||||
// 调用键盘预初始化代码 |
||||
|
||||
// 设置LED引脚为输出模式 |
||||
setPinOutput(B0); |
||||
setPinOutput(B1); |
||||
setPinOutput(B2); |
||||
setPinOutput(B3); |
||||
setPinOutput(B4); |
||||
} |
||||
``` |
||||
|
||||
### `keyboard_pre_init_*` 函数文档 |
||||
|
||||
* 键盘/修订: `void keyboard_pre_init_kb(void)` |
||||
* 布局: `void keyboard_pre_init_user(void)` |
||||
|
||||
## 矩阵初始化代码 |
||||
|
||||
这将会在矩阵初始化时被调用,在某些硬件设置好后,但在一些功能被初始化前。 |
||||
|
||||
这在你设置其他地方会用到的东西的时候会很有用,但与硬件无关,也不依赖于它的启动位置。 |
||||
|
||||
|
||||
### `matrix_init_*`函数文档 |
||||
|
||||
* 键盘/修订: `void matrix_init_kb(void)` |
||||
* 布局: `void matrix_init_user(void)` |
||||
|
||||
|
||||
## 键盘后初始化代码 |
||||
|
||||
这是键盘初始化过程中的最后一个任务。如果您想更改某些特性,这会很有用,因为此时应该对它们进行初始化。 |
||||
|
||||
|
||||
### `keyboard_post_init_user()`示例实现 |
||||
|
||||
本示例在所有初始化完成后运行,配置RGB灯。 |
||||
|
||||
```c |
||||
void keyboard_post_init_user(void) { |
||||
// 调用后初始化代码 |
||||
rgblight_enable_noeeprom(); // 使能Rgb,不保存设置 |
||||
rgblight_sethsv_noeeprom(180, 255, 255); // 将颜色设置到蓝绿色(青色)不保存 |
||||
rgblight_mode_noeeprom(RGBLIGHT_MODE_BREATHING + 3); // 设置快速呼吸模式不保存 |
||||
} |
||||
``` |
||||
|
||||
### `keyboard_post_init_*` 函数文档 |
||||
|
||||
* 键盘/修订: `void keyboard_post_init_kb(void)` |
||||
* 布局: `void keyboard_post_init_user(void)` |
||||
|
||||
# 矩阵扫描代码 |
||||
|
||||
可能的话你要用`process_record_*()`自定义键盘,以这种方式连接到事件中,以确保代码不会对键盘产生负面的性能影响。然而,在极少数情况下,有必要进行矩阵扫描。在这些函数中要特别注意代码的性能,因为它每秒至少被调用10次。 |
||||
|
||||
### `matrix_scan_*`示例实现 |
||||
|
||||
这个例子被故意省略了。在hook这样一个对性能及其敏感的区域之前,您应该足够了解qmk的内部结构,以便在没有示例的情况下编写。如果你需要帮助,请[建立一个issue](https://github.com/qmk/qmk_firmware/issues/new)或[在Discord上与我们交流](https://discord.gg/Uq7gcHh). |
||||
|
||||
### `matrix_scan_*` 函数文档 |
||||
|
||||
* 键盘/修订: `void matrix_scan_kb(void)` |
||||
* 布局: `void matrix_scan_user(void)` |
||||
|
||||
该函数在每次矩阵扫描时被调用,这基本与MCU处理能力上限相同。在这里写代码要谨慎,因为它会运行很多次。 |
||||
|
||||
你会在自定义矩阵扫描代码时用到这个函数。这也可以用作自定义状态输出(比如LED灯或者屏幕)或者其他即便用户不输入你也想定期运行的功能。 |
||||
|
||||
|
||||
# 键盘 空闲/唤醒 代码 |
||||
|
||||
如果键盘支持就可以通过停止一大票功能来达到"空闲"。RGB灯和背光就是很好的例子。这可以节约能耗,也可能让你键盘风味更佳。 |
||||
|
||||
用两个函数控制: `suspend_power_down_*`和`suspend_wakeup_init_*`, 分别在系统板空闲和唤醒时调用。 |
||||
|
||||
|
||||
### suspend_power_down_user()和suspend_wakeup_init_user()示例实现 |
||||
|
||||
|
||||
```c |
||||
void suspend_power_down_user(void) { |
||||
rgb_matrix_set_suspend_state(true); |
||||
} |
||||
|
||||
void suspend_wakeup_init_user(void) { |
||||
rgb_matrix_set_suspend_state(false); |
||||
} |
||||
``` |
||||
|
||||
### 键盘 挂起/唤醒 函数文档 |
||||
|
||||
* 键盘/修订: `void suspend_power_down_kb(void)` 和`void suspend_wakeup_init_user(void)` |
||||
* 布局: `void suspend_power_down_kb(void)` 和 `void suspend_wakeup_init_user(void)` |
||||
|
||||
# 层改变代码 |
||||
|
||||
每当层改变这个就运行代码。这对于层指示或自定义层处理很有用。 |
||||
|
||||
### `layer_state_set_*` 示例实现 |
||||
|
||||
本例使用了Planck键盘示范了如何设置 [RGB背光灯](feature_rgblight.md)使之与层对应 |
||||
|
||||
```c |
||||
uint32_t layer_state_set_user(uint32_t state) { |
||||
switch (biton32(state)) { |
||||
case _RAISE: |
||||
rgblight_setrgb (0x00, 0x00, 0xFF); |
||||
break; |
||||
case _LOWER: |
||||
rgblight_setrgb (0xFF, 0x00, 0x00); |
||||
break; |
||||
case _PLOVER: |
||||
rgblight_setrgb (0x00, 0xFF, 0x00); |
||||
break; |
||||
case _ADJUST: |
||||
rgblight_setrgb (0x7A, 0x00, 0xFF); |
||||
break; |
||||
default: // for any other layers, or the default layer |
||||
rgblight_setrgb (0x00, 0xFF, 0xFF); |
||||
break; |
||||
} |
||||
return state; |
||||
} |
||||
``` |
||||
### `layer_state_set_*` 函数文档 |
||||
|
||||
* 键盘/修订: `uint32_t layer_state_set_kb(uint32_t state)` |
||||
* 布局: `uint32_t layer_state_set_user(uint32_t state)` |
||||
|
||||
|
||||
该`状态`是活动层的bitmask, 详见[布局概述](keymap.md#布局的层状态) |
||||
|
||||
|
||||
# 掉电保存配置 (EEPROM) |
||||
|
||||
这会让你的配置长期的保存在键盘中。这些配置保存在你主控的EEPROM里,掉电不会消失。 设置可以用`eeconfig_read_kb`和`eeconfig_read_user`读取,可以用`eeconfig_update_kb`和`eeconfig_update_user`写入。这对于您希望能够切换的功能很有用(比如切换RGB层指示。此外,你可以用`eeconfig_init_kb`和`eeconfig_init_user`来设置EEPROM默认值。 |
||||
|
||||
最复杂的部分可能是,有很多方法可以通过EEPROM存储和访问数据,并且并没有用哪种方法是“政治正确”的。你每个功能只有一个双字(四字节)空间。 |
||||
|
||||
记住EEPROM是有写入寿命的。尽管写入寿命很高,但是并不是只有设置写道EEPROM中。如果你写入频繁,你的MCU寿命将会变短。 |
||||
|
||||
* 如果您不理解这个例子,那么您可能希望避免使用这个特性,因为它相当复杂。 |
||||
|
||||
### 示例实现 |
||||
|
||||
本例讲解了如何添加设置,并且读写。本里使用了用户布局。这是一个复杂的函数,有很多事情要做。实际上,它使用了很多上述函数来工作! |
||||
|
||||
|
||||
在你的keymap.c文件中,将以下代码添加至顶部: |
||||
```c |
||||
typedef union { |
||||
uint32_t raw; |
||||
struct { |
||||
bool rgb_layer_change :1; |
||||
}; |
||||
} user_config_t; |
||||
|
||||
user_config_t user_config; |
||||
``` |
||||
|
||||
以上代码建立了一个结构体,该结构体可以存储设置并可用于写入EEPROM。如此这般将无需定义变量,因为在结构体中已然定义。要记住`bool` (布尔)值使用1位, `uint8_t`使用8位, `uint16_t`使用16位。你可以混合搭配使用,但是顺序记错可能会招致麻烦,因为那会改变写入写出的值。 |
||||
|
||||
`layer_state_set_*`函数中使用了`rgb_layer_change`,使用了`keyboard_post_init_user`和`process_record_user`来配置一切。 |
||||
|
||||
首先要使用`keyboard_post_init_user,你要加入`eeconfig_read_user()`来填充你刚刚创建的结构体。然后您可以立即使用这个结构来控制您的布局中的功能。就像这样: |
||||
```c |
||||
void keyboard_post_init_user(void) { |
||||
// 调用布局级别的矩阵初始化 |
||||
|
||||
// 从EEPROM读用户配置 |
||||
user_config.raw = eeconfig_read_user(); |
||||
|
||||
// 如使能,设置默认层 |
||||
if (user_config.rgb_layer_change) { |
||||
rgblight_enable_noeeprom(); |
||||
rgblight_sethsv_noeeprom_cyan(); |
||||
rgblight_mode_noeeprom(1); |
||||
} |
||||
} |
||||
``` |
||||
以上函数会在读EEPROM配置后立即使用该设置来设置默认层RGB颜色。"raw"的值是从你上面基于"union"创建的结构体中转换来的。 |
||||
|
||||
```c |
||||
uint32_t layer_state_set_user(uint32_t state) { |
||||
switch (biton32(state)) { |
||||
case _RAISE: |
||||
if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom_magenta(); rgblight_mode_noeeprom(1); } |
||||
break; |
||||
case _LOWER: |
||||
if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom_red(); rgblight_mode_noeeprom(1); } |
||||
break; |
||||
case _PLOVER: |
||||
if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom_green(); rgblight_mode_noeeprom(1); } |
||||
break; |
||||
case _ADJUST: |
||||
if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom_white(); rgblight_mode_noeeprom(1); } |
||||
break; |
||||
default: // 针对其他层或默认层 |
||||
if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom_cyan(); rgblight_mode_noeeprom(1); } |
||||
break; |
||||
} |
||||
return state; |
||||
} |
||||
``` |
||||
这样仅在值使能时会改变RGB背光灯。现在配置这个值, 为`process_record_user`创建一个新键码叫做`RGB_LYR`。我们要确保,如果使用正常的RGB代码,使用上面的示例将其关闭,请将其设置为: |
||||
```c |
||||
|
||||
bool process_record_user(uint16_t keycode, keyrecord_t *record) { |
||||
switch (keycode) { |
||||
case FOO: |
||||
if (record->event.pressed) { |
||||
// 按下时做点什么 |
||||
} else { |
||||
// 释放时做点什么 |
||||
} |
||||
return false; // 跳过此键的进一步处理 |
||||
case KC_ENTER: |
||||
// 在按下回车时播放音符 |
||||
if (record->event.pressed) { |
||||
PLAY_NOTE_ARRAY(tone_qwerty); |
||||
} |
||||
return true; // 让QMK产生回车按下/释放事件 |
||||
case RGB_LYR: // 本句让underglow作为层指示,或正常使用。 |
||||
if (record->event.pressed) { |
||||
user_config.rgb_layer_change ^= 1; // 切换状态 |
||||
eeconfig_update_user(user_config.raw); // 向EEPROM写入新状态 |
||||
if (user_config.rgb_layer_change) { // 如果层状态被使能 |
||||
layer_state_set(layer_state); // 那么立刻更新层颜色 |
||||
} |
||||
} |
||||
return false; break; |
||||
case RGB_MODE_FORWARD ... RGB_MODE_GRADIENT: // 对于所有的RGB代码 (see quantum_keycodes.h, L400 可以参考) |
||||
if (record->event.pressed) { //本句失能层指示,假设你改变了这个…你要把它禁用 |
||||
if (user_config.rgb_layer_change) { // 仅当使能时 |
||||
user_config.rgb_layer_change = false; // 失能,然后 |
||||
eeconfig_update_user(user_config.raw); // 向EEPROM写入设置 |
||||
} |
||||
} |
||||
return true; break; |
||||
default: |
||||
return true; // 按其他键正常 |
||||
} |
||||
} |
||||
``` |
||||
最后你要加入`eeconfig_init_user`函数,所以当EEPROM重置时,可以指定默认值, 甚至自定义操作。想强制重置EEPROM,请用`EEP_RST`键码或[Bootmagic](feature_bootmagic.md)函数。比如,如果要在默认情况下设置RGB层指示,并保存默认值 |
||||
|
||||
```c |
||||
void eeconfig_init_user(void) { // EEPROM正被重置 |
||||
user_config.raw = 0; |
||||
user_config.rgb_layer_change = true; // 我们想要默认使能 |
||||
eeconfig_update_user(user_config.raw); // 向EEPROM写入默认值 |
||||
|
||||
// use the non noeeprom versions, 还要向EEPROM写入这些值 |
||||
rgblight_enable(); // 默认使能RGB |
||||
rgblight_sethsv_cyan(); // 默认设置青色 |
||||
rgblight_mode(1); // 默认设置长亮 |
||||
} |
||||
``` |
||||
|
||||
然后就完事了。RGB层指示会在你想让它工作时工作。这个设置会一直保存,即便你拔下键盘。如果你使用其他RGB代码,层指示将失能,现在它可以做你所想了。 |
||||
|
||||
### 'EECONFIG' 函数文档 |
||||
|
||||
* 键盘/修订: `void eeconfig_init_kb(void)`, `uint32_t eeconfig_read_kb(void)`和`void eeconfig_update_kb(uint32_t val)` |
||||
* 布局: `void eeconfig_init_user(void)`, `uint32_t eeconfig_read_user(void)`和`void eeconfig_update_user(uint32_t val)` |
||||
|
||||
`val` 是你想写入EEPROM的值,`eeconfig_read_*`函数会从EEPROM返回一个32位(双字)的值。 |
||||
|
||||
# 自定义击键-长按临界值(TAPPING_TERM) |
||||
默认情况下,击键-长按临界值是全球统一的,并且不能通过键进行配置。对于大多数用户来说这很好。但是在有些情况下,对于`LT`键来说按键延时对双功能键的提升更大,可能是因为有些键比其他的键更容易按住。为了不给每个都自定义键码,本功能可以为每个键定义`TAPPING_TERM`。 |
||||
|
||||
想使能这个功能的话, 要先在`config.h`加上`#define TAPPING_TERM_PER_KEY`。 |
||||
|
||||
|
||||
## `get_tapping_term`示例实现 |
||||
|
||||
想要修改基于键码的`TAPPING TERM`,你要向`keymap.c`文件添加如下代码: |
||||
|
||||
```c |
||||
uint16_t get_tapping_term(uint16_t keycode) { |
||||
switch (keycode) { |
||||
case SFT_T(KC_SPC): |
||||
return TAPPING_TERM + 1250; |
||||
case LT(1, KC_GRV): |
||||
return 130; |
||||
default: |
||||
return TAPPING_TERM; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
### `get_tapping_term` 函数文档 |
||||
|
||||
不像这篇的其他功能,这个不需要quantum或者键盘级别的函数,只要用户级函数即可。 |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue