QMK - 2
QMK's source code can be found at Github: https://github.com/qmk/qmk_firmware.
Learn QMK's source code
Basic project structure
There are three logic levels in QMK project:
Core(_quantum)
-- Keyboard/Revision(_kb)
-- Keymap(_user)
In QMK, many custom functions have a _kb
or _user
suffix. By convention, when you customize your keyboard or a revision of your keyboard, using _kb
functions. And when you customize your keymap, use _user
functions.
Remember to call _user
function at the beginning of your _kb
functions. Otherwise, those _user
functions won't be execute any more.
Program Entry
Like other C programs, QMK's entry is a main()
function. QMK's main function is at quantum/main.c
, which is the entrance of all the QMK firmware.
QMK's main()
function is quite simple: setup platform/protocol/keyboard and then run the infinite main loop. The main loop will call protocol_task()
, then the keyboard_task()
in quantum/keyboard.c
is called. keyboard_task()
is where the keyboard specific functionality is dispatched, such as matrix scanning, mouse handling and controlling keyboard status LEDs.
Matrix Scanning
Matrix scanning is the core of a keyboard firmware. QMK provides built in scanning algorithm, you just need to define your matrix layout.
To declare your own key matrix, a C macro is used. For example, to define a 2*2 matrix, you can use the following code
|
|
Note that you may not have a key at every position in the matrix, you can use keycode KC_NO
in the second part of the macro. A typical numpad layout can be defined using the following code:
|
|
In keymap, you can use this macro to map keycodes of actual physical keys to matrix keys:
|
|
You can see the keymap has 3 dimensions. The first dimension is actual layer. Each layer has MATRIX_ROWS
* MATRIX_COLS
keys. In the given example, we defined only one layer.
Detect key strokes
At each matrix scanning loop, the matrix scanning function returns the current state of the matrix. QMK stores the result of last matrix scan, and compares with the current scanning result to determine which key is pressed or released. The the key code is dispatched to process_record()
function.
Process Record
Function process_record()
is not complex, it contains a chain of events(c functions). Many of then depends on rules defined in rules.mk
. The full events list is here. If any of them returns false
, the following functions won't be executed.
Customize keymap
You can create your own key code with QMK. To create your key code, you need to define an enum in keymap.c
first:
|
|
QMK provides a macro SAFE_RANGE
which ensure that you got a unique and correct key code.
Program a key
When you want to overwrite your key's function or define the functionality of your new keycode, you can use process_record_kb()
or process_record_user()
. Remember this process_record()
function? Your customized process_record_kb/user()
function is similar: return false if you want to overwrite this key or attach new functionality to the key, otherwise just return true. Here is an example:
|
|
The definition of input param record
:
|
|
record
has the input key's col/row, whether it's pressed and the press time.
Keyboard Initialization
You can also customize the initialization process of the keyboard. There are 3 functions that you can overwrite:
keyboard_pre_init_*
: happens at the early start of the firmware's setup process, can be used to initialize your hardwarematrix_init_*
: happens midway of the firmware's setup process. At this moment, hardware is initialized, but features may not be yetkeyboard_post_init_*
: happens at the end of the firmware's setup process. Hardware and most features are ready, you should put most of your customization code here
Keyboard Pre Initialization
keyboard_pre_init_*
is used to initialize your own extra hardwares. Note that this process starts very early -- even earlier than the USB starts. The following is an example to set up LEDs using keyboard_pre_init_*
:
|
|
Matrix Initialization Code
matrix_init_*
is called when the matrix is initialized, you can overwrite the low-level matrix configuration here. If you want to change the default pin initialization method, you can use the following method:
void matrix_init_pins(void)
: GPIO pin initialization. By default it will initialize pins set inMATRIX_ROW_PINS
andMATRIX_COL_PINS
, and the setup method is defined usingROW2COL
,COL2ROW
orDIRECT_PINS
.
Keyboard Post Initialization Code
You most customization code should be wrote here. At this moment, most hardware and features are initialized, you can take changes to certain features as you want.
The following is an example about setting up RGB lights:
|
|
QMK configuration files
We've already knew that there are some configuration files in QMK when you're going to create your own keyboard, such as info.json
, rules.mk
. In this section, we'll learn how to customize those files.
NOTE: there are many configs that you can set at multiple files. In this case, when you try to compile the firmware, qmk would complain and tell you which config will be used:
Prune your configurations accordingly.
info.json
You can config the hardware and enabled features in info.json
. The full reference of info.json
can be found here: https://docs.qmk.fm/#/reference_info_json.
Hardware configuration
At the top of info.json
, there are some configs about the manufacturer and hardware that you can customize:
|
|
in which, the manufacturer
and keyboard_name
will be displayed in the list of USB devices on Windows and MacOS. You can also choose your USB's vid and pid.
Matrix configuration
You can also define the GPIO pins which are used for matrix scanning in info.json
:
|
|
Then declare your diode direction in your PCB:
|
|
Layout configuration
Next you can config the layout of your keyboard:
|
|
For more available configurations, see: https://docs.qmk.fm/#/reference_info_json
config.h
config.h
is the basic header of the firmware. All configurations here are persist over the whole project. There are lots of configurations available, see this document.
I list some most commonly used configs in config.h
here:
|
|
rules.mk
rules.mk
defines some built options when compiling the firmware. It's actually a makefile which can be recognized with tools like cmake
. In rules.mk
you can set building configurations, MCU options and enable certain features.
Build Options
FIRMWARE_FORMAT
: defines the compiled firmware's format, like.hex
or.bin
SRC
: added sources files for compilation/linkingLAYOUTS
: a list of layouts that this keyboard supports. See Matrix Scanning section.LTO_ENABLE
: add it to reduce the size of your firmware
Feature Options
You can also enable/disable many features such as MAGIC Actions, AUDIO, RGBLIGHT, etc. in rules.mk
. The full list can be found here: https://docs.qmk.fm/#/config_options?id=feature-options.
Here is an example of rules.mk
of owlab suit80:
|
|