mirror of
https://github.com/qmk/qmk_firmware.git
synced 2024-11-21 19:09:25 +00:00
Compare commits
40 Commits
6a87aafe7b
...
8ad35b5c38
Author | SHA1 | Date | |
---|---|---|---|
|
8ad35b5c38 | ||
|
36b5559b99 | ||
|
39161b9ee7 | ||
|
074bbbfb21 | ||
|
638b22d057 | ||
|
9bea332a21 | ||
|
8cbcdcac62 | ||
|
88afd53b1f | ||
|
db4d68fcd9 | ||
|
41fbc685b3 | ||
|
189bb73478 | ||
|
3b2529ccd2 | ||
|
6d9c10235f | ||
|
6451272edb | ||
|
75b745a4cd | ||
|
7db0d87ea8 | ||
|
32d6a621d8 | ||
|
132d2133c3 | ||
|
30fbdb7a68 | ||
|
b8a1b747e8 | ||
|
93bf884829 | ||
|
9057f11b62 | ||
|
ec43ced08d | ||
|
f6772f9225 | ||
|
d392a2728a | ||
|
9d9f273e7b | ||
|
62711d8858 | ||
|
8e92cd27cf | ||
|
6c3540fb4e | ||
|
904fecdd04 | ||
|
94f3d2d6d6 | ||
|
bbfb5cc2cd | ||
|
3ffd5f9a41 | ||
|
61577fc643 | ||
|
5122953194 | ||
|
09d9486052 | ||
|
e3630cdbb2 | ||
|
1a520b5890 | ||
|
ee7a826d3a | ||
|
2d7ca71184 |
@ -215,7 +215,7 @@ else
|
||||
COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/flash
|
||||
COMMON_VPATH += $(DRIVER_PATH)/flash
|
||||
SRC += eeprom_driver.c eeprom_legacy_emulated_flash.c legacy_flash_ops.c
|
||||
else ifneq ($(filter $(MCU_SERIES),STM32F1xx STM32F3xx STM32F4xx STM32L4xx STM32G4xx WB32F3G71xx WB32FQ95xx GD32VF103),)
|
||||
else ifneq ($(filter $(MCU_SERIES),STM32F1xx STM32F3xx STM32F4xx STM32L4xx STM32G4xx WB32F3G71xx WB32FQ95xx AT32F415 GD32VF103),)
|
||||
# Wear-leveling EEPROM implementation, backed by MCU flash
|
||||
OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_WEAR_LEVELING
|
||||
SRC += eeprom_driver.c eeprom_wear_leveling.c
|
||||
|
@ -36,6 +36,7 @@ GENERIC_FEATURES = \
|
||||
HAPTIC \
|
||||
KEY_LOCK \
|
||||
KEY_OVERRIDE \
|
||||
LAYER_LOCK \
|
||||
LEADER \
|
||||
MAGIC \
|
||||
MOUSEKEY \
|
||||
|
@ -3,5 +3,12 @@
|
||||
"0x7C20": "!delete!", // old QK_OUTPUT_AUTO
|
||||
"0x7C21": "!delete!", // old QK_OUTPUT_USB
|
||||
"0x7C22": "!delete!", // old QK_OUTPUT_BLUETOOTH
|
||||
"0x7C7B": {
|
||||
"group": "quantum",
|
||||
"key": "QK_LAYER_LOCK",
|
||||
"aliases": [
|
||||
"QK_LLCK"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,9 @@
|
||||
"WEAR_LEVELING_BACKING_SIZE": {"info_key": "eeprom.wear_leveling.backing_size", "value_type": "int", "to_json": false},
|
||||
"WEAR_LEVELING_LOGICAL_SIZE": {"info_key": "eeprom.wear_leveling.logical_size", "value_type": "int", "to_json": false},
|
||||
|
||||
// Layer locking
|
||||
"LAYER_LOCK_IDLE_TIMEOUT": {"info_key": "layer_lock.timeout", "value_type": "int"},
|
||||
|
||||
// Indicators
|
||||
"LED_CAPS_LOCK_PIN": {"info_key": "indicators.caps_lock"},
|
||||
"LED_NUM_LOCK_PIN": {"info_key": "indicators.num_lock"},
|
||||
|
@ -92,6 +92,7 @@
|
||||
"GD32VF103",
|
||||
"WB32F3G71",
|
||||
"WB32FQ95",
|
||||
"AT32F415",
|
||||
"atmega16u2",
|
||||
"atmega32u2",
|
||||
"atmega16u4",
|
||||
@ -216,6 +217,7 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"apm32-dfu",
|
||||
"at32-dfu",
|
||||
"atmel-dfu",
|
||||
"bootloadhid",
|
||||
"caterina",
|
||||
@ -373,6 +375,12 @@
|
||||
}
|
||||
},
|
||||
"keycodes": {"$ref": "qmk.definitions.v1#/keycode_decl_array"},
|
||||
"layer_lock": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"}
|
||||
}
|
||||
},
|
||||
"layout_aliases": {
|
||||
"type": "object",
|
||||
"additionalProperties": {"$ref": "qmk.definitions.v1#/layout_macro"}
|
||||
|
@ -123,6 +123,7 @@
|
||||
{ "text": "Key Lock", "link": "/features/key_lock" },
|
||||
{ "text": "Key Overrides", "link": "/features/key_overrides" },
|
||||
{ "text": "Layers", "link": "/feature_layers" },
|
||||
{ "text": "Layer Lock", "link": "/features/layer_lock" },
|
||||
{ "text": "One Shot Keys", "link": "/one_shot_keys" },
|
||||
{ "text": "OS Detection", "link": "/features/os_detection" },
|
||||
{ "text": "Raw HID", "link": "/features/rawhid" },
|
||||
|
@ -57,6 +57,10 @@ You can also use any ARM chip with USB that [ChibiOS](https://www.chibios.org) s
|
||||
* [WB32F3G71xx](http://www.westberrytech.com)
|
||||
* [WB32FQ95xx](http://www.westberrytech.com)
|
||||
|
||||
### Artery (AT32)
|
||||
|
||||
* [AT32F415](https://www.arterychip.com/en/product/AT32F415.jsp)
|
||||
|
||||
### NXP (Kinetis)
|
||||
|
||||
* [MKL26Z64](https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/kl-series-cortex-m0-plus/kinetis-kl2x-72-96-mhz-usb-ultra-low-power-microcontrollers-mcus-based-on-arm-cortex-m0-plus-core:KL2x)
|
||||
|
@ -98,6 +98,7 @@ The device name here is the name that appears in Zadig, and may not be what the
|
||||
|`bootloadhid` |HIDBoot |`16C0:05DF` |HidUsb |
|
||||
|`usbasploader`|USBasp |`16C0:05DC` |libusbK|
|
||||
|`apm32-dfu` |APM32 DFU ISP Mode |`314B:0106` |WinUSB |
|
||||
|`at32-dfu` |AT32 Bootloader DFU |`2E3C:DF11` |WinUSB |
|
||||
|`stm32-dfu` |STM32 BOOTLOADER |`0483:DF11` |WinUSB |
|
||||
|`gd32v-dfu` |GD32V BOOTLOADER |`28E9:0189` |WinUSB |
|
||||
|`kiibohd` |Kiibohd DFU Bootloader |`1C11:B007` |WinUSB |
|
||||
|
@ -17,6 +17,9 @@ These functions allow you to activate layers in various ways. Note that layers a
|
||||
* `TO(layer)` - activates *layer* and de-activates all other layers (except your default layer). This function is special, because instead of just adding/removing one layer to your active layer stack, it will completely replace your current active layers, uniquely allowing you to replace higher layers with a lower one. This is activated on keydown (as soon as the key is pressed).
|
||||
* `TT(layer)` - Layer Tap-Toggle. If you hold the key down, *layer* is activated, and then is de-activated when you let go (like `MO`). If you repeatedly tap it, the layer will be toggled on or off (like `TG`). It needs 5 taps by default, but you can change this by defining `TAPPING_TOGGLE` -- for example, `#define TAPPING_TOGGLE 2` to toggle on just two taps.
|
||||
|
||||
See also the [Layer Lock key](features/layer_lock), which locks the highest
|
||||
active layer until pressed again.
|
||||
|
||||
### Caveats {#caveats}
|
||||
|
||||
Currently, the `layer` argument of `LT()` is limited to layers 0-15, and the `kc` argument to the [Basic Keycode set](keycodes_basic), meaning you can't use keycodes like `LCTL()`, `KC_TILD`, or anything greater than `0xFF`. This is because QMK uses 16-bit keycodes, of which 4 bits are used for the function identifier and 4 bits for the layer, leaving only 8 bits for the keycode.
|
||||
|
139
docs/features/layer_lock.md
Normal file
139
docs/features/layer_lock.md
Normal file
@ -0,0 +1,139 @@
|
||||
# Layer Lock
|
||||
|
||||
Some [layer switches](../feature_layers#switching-and-toggling-layers) access
|
||||
the layer by holding the key, including momentary layer `MO(layer)` and layer
|
||||
tap `LT(layer, key)` keys. You may sometimes need to stay on the layer for a
|
||||
long period of time. Layer Lock "locks" the current layer to stay on, supposing
|
||||
it was accessed by one of:
|
||||
|
||||
* `MO(layer)` momentary layer switch
|
||||
* `LT(layer, key)` layer tap
|
||||
* `OSL(layer)` one-shot layer
|
||||
* `TT(layer)` layer tap toggle
|
||||
* `LM(layer, mod)` layer-mod key (the layer is locked, but not the mods)
|
||||
|
||||
Press the Layer Lock key again to unlock the layer. Additionally, when a layer
|
||||
is locked, layer switch keys that turn off the layer such as `TO(other_layer)`
|
||||
will unlock it.
|
||||
|
||||
|
||||
## How do I enable Layer Lock
|
||||
|
||||
In your rules.mk, add:
|
||||
|
||||
```make
|
||||
LAYER_LOCK_ENABLE = yes
|
||||
```
|
||||
|
||||
Pick a key in your keymap on a layer you intend to lock, and assign it the
|
||||
keycode `QK_LAYER_LOCK` (short alias `QK_LLCK`). Note that locking the base
|
||||
layer has no effect, so typically, this key is used on layers above the base
|
||||
layer.
|
||||
|
||||
|
||||
## Example use
|
||||
|
||||
Consider a keymap with the following base layer.
|
||||
|
||||
![Base layer with a MO(NAV) key.](https://i.imgur.com/DkEhj9x.png)
|
||||
|
||||
The highlighted key is a momentary layer switch `MO(NAV)`. Holding it accesses a
|
||||
navigation layer.
|
||||
|
||||
![Nav layer with a Layer Lock key.](https://i.imgur.com/2wUZNWk.png)
|
||||
|
||||
|
||||
Holding the NAV key is fine for brief use, but awkward to continue holding when
|
||||
using navigation functions continuously. The Layer Lock key comes to the rescue:
|
||||
|
||||
1. Hold the NAV key, activating the navigation layer.
|
||||
2. Tap Layer Lock.
|
||||
3. Release NAV. The navigation layer stays on.
|
||||
4. Make use of the arrow keys, etc.
|
||||
5. Tap Layer Lock or NAV again to turn the navigation layer back off.
|
||||
|
||||
A variation that would also work is to put the Layer Lock key on the base layer
|
||||
and make other layers transparent (`KC_TRNS`) in that position. Pressing the
|
||||
Layer Lock key locks (or unlocks) the highest active layer, regardless of which
|
||||
layer the Layer Lock key is on.
|
||||
|
||||
|
||||
## Idle timeout
|
||||
|
||||
Optionally, Layer Lock may be configured to unlock if the keyboard is idle
|
||||
for some time. In config.h, define `LAYER_LOCK_IDLE_TIMEOUT` in units of
|
||||
milliseconds:
|
||||
|
||||
```c
|
||||
#define LAYER_LOCK_IDLE_TIMEOUT 60000 // Turn off after 60 seconds.
|
||||
```
|
||||
|
||||
|
||||
## Functions
|
||||
|
||||
Use the following functions to query and manipulate the layer lock state.
|
||||
|
||||
| Function | Description |
|
||||
|----------------------------|------------------------------------|
|
||||
| `is_layer_locked(layer)` | Checks whether `layer` is locked. |
|
||||
| `layer_lock_on(layer)` | Locks and turns on `layer`. |
|
||||
| `layer_lock_off(layer)` | Unlocks and turns off `layer`. |
|
||||
| `layer_lock_invert(layer)` | Toggles whether `layer` is locked. |
|
||||
|
||||
|
||||
## Representing the current Layer Lock state
|
||||
|
||||
There is an optional callback `layer_lock_set_user()` that gets called when a
|
||||
layer is locked or unlocked. This is useful to represent the current lock state
|
||||
for instance by setting an LED. In keymap.c, define
|
||||
|
||||
```c
|
||||
bool layer_lock_set_user(layer_state_t locked_layers) {
|
||||
// Do something like `set_led(is_layer_locked(NAV));`
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
The argument `locked_layers` is a bitfield in which the kth bit is on if the kth
|
||||
layer is locked. Alternatively, you can use `is_layer_locked(layer)` to check if
|
||||
a given layer is locked.
|
||||
|
||||
|
||||
## Combine Layer Lock with a mod-tap
|
||||
|
||||
It is possible to create a [mod-tap MT key](../mod_tap) that acts as a modifier
|
||||
on hold and Layer Lock on tap. Since Layer Lock is not a [basic
|
||||
keycode](../keycodes_basic), attempting `MT(mod, QK_LLCK)` is invalid does not
|
||||
work directly, yet this effect can be achieved through [changing the tap
|
||||
function](../mod_tap#changing-tap-function). For example, the following
|
||||
implements a `SFTLLCK` key that acts as Shift on hold and Layer Lock on tap:
|
||||
|
||||
```c
|
||||
#define SFTLLCK LSFT_T(KC_0)
|
||||
|
||||
// Use SFTLLCK in your keymap...
|
||||
|
||||
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
|
||||
switch (keycode) {
|
||||
case SFTLLCK:
|
||||
if (record->tap.count) {
|
||||
if (record->event.pressed) {
|
||||
// Toggle the lock on the highest layer.
|
||||
layer_lock_invert(get_highest_layer(layer_state));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
// Other macros...
|
||||
}
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
In the above, `KC_0` is an arbitrary placeholder for the tapping keycode. This
|
||||
keycode will never be sent, so any basic keycode will do. In
|
||||
`process_record_user()`, the tap press event is changed to toggle the lock on
|
||||
the highest layer. Layer Lock can be combined with a [layer-tap LT
|
||||
key](../feature_layers#switching-and-toggling-layers) similarly.
|
||||
|
@ -345,6 +345,39 @@ Flashing sequence:
|
||||
3. Flash a .bin file
|
||||
4. Reset the device into application mode (may be done automatically)
|
||||
|
||||
## AT32 DFU
|
||||
|
||||
All AT32 MCUs come preloaded with a factory bootloader that cannot be modified nor deleted.
|
||||
|
||||
To ensure compatibility with the AT32-DFU bootloader, make sure this block is present in your `rules.mk`:
|
||||
|
||||
```make
|
||||
# Bootloader selection
|
||||
BOOTLOADER = at32-dfu
|
||||
```
|
||||
|
||||
Compatible flashers:
|
||||
|
||||
* [dfu-util](https://dfu-util.sourceforge.net/) / `:dfu-util` target in QMK (recommended command line)
|
||||
```
|
||||
dfu-util -a 0 -d 2E3C:DF11 -s 0x8000000:leave -D <filename>
|
||||
```
|
||||
|
||||
Flashing sequence:
|
||||
|
||||
1. Enter the bootloader using any of the following methods:
|
||||
* Tap the `QK_BOOT` keycode
|
||||
* If a reset circuit is present, tap the `RESET` button on the PCB; some boards may also have a toggle switch that must be flipped
|
||||
* Otherwise, you need to bridge `BOOT0` to VCC (via `BOOT0` button or jumper), short `RESET` to GND (via `RESET` button or jumper), and then let go of the `BOOT0` bridge
|
||||
2. Wait for the OS to detect the device
|
||||
3. Flash a .bin file
|
||||
4. Reset the device into application mode (may be done automatically)
|
||||
|
||||
### `make` Targets
|
||||
|
||||
* `:dfu-util`: Waits until an AT32 bootloader device is available, and then flashes the firmware.
|
||||
* `:dfu-util-split-left` and `:dfu-util-split-right`: Flashes the firmware as with `:dfu-util`, but also sets the handedness setting in EEPROM.
|
||||
|
||||
## tinyuf2
|
||||
|
||||
Keyboards may opt into supporting the tinyuf2 bootloader. This is currently only supported on F303/F401/F411.
|
||||
|
@ -387,6 +387,14 @@ See also: [Key Lock](features/key_lock)
|
||||
|---------|--------------------------------------------------------------|
|
||||
|`QK_LOCK`|Hold down the next key pressed, until the key is pressed again|
|
||||
|
||||
## Layer Lock {#layer-lock}
|
||||
|
||||
See also: [Layer Lock](features/layer_lock)
|
||||
|
||||
|Key |Aliases |Description |
|
||||
|---------------|---------|----------------------------------|
|
||||
|`QK_LAYER_LOCK`|`QK_LLCK`|Locks or unlocks the highest layer|
|
||||
|
||||
## Layer Switching {#layer-switching}
|
||||
|
||||
See also: [Layer Switching](feature_layers#switching-and-toggling-layers)
|
||||
|
@ -4,52 +4,51 @@ This page describes the support for [Compatible Microcontrollers](compatible_mic
|
||||
|
||||
If you have not yet you should read the [Keyboard Guidelines](hardware_keyboard_guidelines) to get a sense of how keyboards fit into QMK.
|
||||
|
||||
|
||||
QMK has a number of features to simplify working with keyboards. For most, you don't have to write a single line of code. To get started, run `qmk new-keyboard`:
|
||||
|
||||
```
|
||||
$ qmk new-keyboard
|
||||
Ψ Generating a new QMK keyboard directory
|
||||
|
||||
Name Your Keyboard Project
|
||||
For more infomation, see:
|
||||
https://docs.qmk.fm/hardware_keyboard_guidelines#naming-your-keyboardproject
|
||||
Ψ Name Your Keyboard Project
|
||||
Ψ For more information, see:
|
||||
https://docs.qmk.fm/hardware_keyboard_guidelines#naming-your-keyboard-project
|
||||
Keyboard Name? mycoolkeeb
|
||||
Ψ Attribution
|
||||
Ψ Used for maintainer, copyright, etc.
|
||||
Your GitHub Username? [jsmith]
|
||||
Ψ More Attribution
|
||||
Ψ Used for maintainer, copyright, etc.
|
||||
Your Real Name? [John Smith]
|
||||
Ψ Pick Base Layout
|
||||
Ψ As a starting point, one of the common layouts can be used to
|
||||
bootstrap the process
|
||||
Default Layout?
|
||||
1. 60_abnt2
|
||||
...
|
||||
65. none of the above
|
||||
Please enter your choice: [65]
|
||||
Ψ What Powers Your Project
|
||||
Ψ Is your board using a separate development board, such as a Pro Micro,
|
||||
or is the microcontroller integrated onto the PCB?
|
||||
|
||||
keyboard Name? mycoolkeeb
|
||||
|
||||
Attribution
|
||||
Used for maintainer, copyright, etc
|
||||
|
||||
Your GitHub Username? [jsmith]
|
||||
|
||||
More Attribution
|
||||
Used for maintainer, copyright, etc
|
||||
|
||||
Your Real Name? [John Smith]
|
||||
|
||||
Pick Base Layout
|
||||
As a starting point, one of the common layouts can be used to bootstrap the process
|
||||
|
||||
Default Layout?
|
||||
1. 60_ansi
|
||||
...
|
||||
50. tkl_iso
|
||||
51. none of the above
|
||||
Please enter your choice: [51]
|
||||
|
||||
What Powers Your Project
|
||||
For more infomation, see:
|
||||
https://docs.qmk.fm/#/compatible_microcontrollers
|
||||
|
||||
MCU?
|
||||
1. atmega32u4
|
||||
...
|
||||
22. STM32F303
|
||||
Please enter your choice: [12]
|
||||
For more information, see:
|
||||
https://docs.qmk.fm/compatible_microcontrollers
|
||||
Using a Development Board? [y/n] y
|
||||
Ψ Select Development Board
|
||||
Ψ For more information, see:
|
||||
https://docs.qmk.fm/compatible_microcontrollers
|
||||
Development Board?
|
||||
1. bit_c_pro
|
||||
...
|
||||
14. promicro
|
||||
...
|
||||
18. svlinky
|
||||
Please enter your choice: [14]
|
||||
Ψ Created a new keyboard called mycoolkeeb.
|
||||
Ψ To start working on things, `cd` into keyboards/mycoolkeeb,
|
||||
Ψ or open the directory in your preferred text editor.
|
||||
Ψ And build with qmk compile -kb mycoolkeeb -km default.
|
||||
Ψ Build Command: qmk compile -kb mycoolkeeb -km default.
|
||||
Ψ Project Location: /Users/jsmith/qmk_firmware/keyboards/mycoolkeeb.
|
||||
Ψ Now update the config files to match the hardware!
|
||||
```
|
||||
|
||||
This will create all the files needed to support your new keyboard, and populate the settings with default values. Now you just need to customize it for your keyboard.
|
||||
@ -58,13 +57,13 @@ This will create all the files needed to support your new keyboard, and populate
|
||||
|
||||
This is where you'll describe your keyboard. Please follow the [Keyboard Readme Template](documentation_templates#keyboard-readmemd-template) when writing your `readme.md`. You're encouraged to place an image at the top of your `readme.md`, please use an external service such as [Imgur](https://imgur.com) to host the images.
|
||||
|
||||
## `info.json`
|
||||
## `keyboard.json`
|
||||
|
||||
The `info.json` file is where you configure the hardware and feature set for your keyboard. There are a lot of options that can be placed in that file, too many to list here. For a complete overview of available options see the [Data Driven Configuration Options](reference_info_json) page.
|
||||
The `keyboard.json` file is where you configure the hardware and feature set for your keyboard. There are a lot of options that can be placed in that file, too many to list here. For a complete overview of available options see the [Data Driven Configuration Options](reference_info_json) page.
|
||||
|
||||
### Hardware Configuration
|
||||
|
||||
At the top of the `info.json` you'll find USB related settings. These control how your keyboard appears to the Operating System. If you don't have a good reason to change you should leave the `usb.vid` as `0xFEED`. For the `usb.pid` you should pick a number that is not yet in use.
|
||||
At the top of the `keyboard.json` you'll find USB related settings. These control how your keyboard appears to the Operating System. If you don't have a good reason to change you should leave the `usb.vid` as `0xFEED`. For the `usb.pid` you should pick a number that is not yet in use.
|
||||
|
||||
Do change the `manufacturer` and `keyboard_name` lines to accurately reflect your keyboard.
|
||||
|
||||
@ -82,10 +81,11 @@ Do change the `manufacturer` and `keyboard_name` lines to accurately reflect you
|
||||
Windows and macOS will display the `manufacturer` and `keyboard_name` in the list of USB devices. `lsusb` on Linux instead prefers the values in the list maintained by the [USB ID Repository](http://www.linux-usb.org/usb-ids.html). By default, it will only use `manufacturer` and `keyboard_name` if the list does not contain that `usb.vid` / `usb.pid`. `sudo lsusb -v` will show the values reported by the device, and they are also present in kernel logs after plugging it in.
|
||||
:::
|
||||
|
||||
|
||||
### Matrix Configuration
|
||||
|
||||
The next section of the `info` file deals with your keyboard's matrix. The first thing you should define is which pins on your MCU are connected to rows and columns. To do so simply specify the names of those pins:
|
||||
The next section of the `keyboard.json` deals with your keyboard's matrix. The first thing you should define is which pins on your MCU are connected to rows and columns. To do so simply specify the names of those pins:
|
||||
|
||||
#### Diode Matrix
|
||||
|
||||
```json
|
||||
"matrix_pins": {
|
||||
@ -94,7 +94,7 @@ The next section of the `info` file deals with your keyboard's matrix. The first
|
||||
},
|
||||
```
|
||||
|
||||
The size of the `matrix_pins.cols` and `matrix_pins.rows` arrays infer the size of the matrix (previously `MATRIX_ROWS` and `MATRIX_COLS`).
|
||||
The matrix dimensions are inferred from the length of the `matrix_pins.cols` and `matrix_pins.rows` arrays (previously specified explicitly in `config.h` with `MATRIX_ROWS` and `MATRIX_COLS`).
|
||||
|
||||
Finally, you can specify the direction your diodes point. This can be `COL2ROW` or `ROW2COL`.
|
||||
|
||||
@ -103,54 +103,56 @@ Finally, you can specify the direction your diodes point. This can be `COL2ROW`
|
||||
```
|
||||
|
||||
#### Direct Pin Matrix
|
||||
To configure a keyboard where each switch is connected to a separate pin and ground instead of sharing row and column pins, use `matrix_pins.direct`. The mapping defines the pins of each switch in rows and columns, from left to right. The size of the `matrix_pins.direct` array infers the size of the matrix. Use `NO_PIN` to fill in blank spaces. Overrides the behaviour of `diode_direction`, `matrix_pins.cols` and `matrix_pins.rows`.
|
||||
|
||||
To configure a keyboard where each switch is connected to a separate pin and ground instead of sharing row and column pins, use `matrix_pins.direct`. This overrides the behaviour of `diode_direction`, `matrix_pins.cols` and `matrix_pins.rows`, and they should not be specified together.
|
||||
|
||||
```json
|
||||
"matrix_pins": {
|
||||
"direct": [
|
||||
["F1", "E6", "B0", "B2", "B3" ],
|
||||
["F5", "F0", "B1", "B7", "D2" ],
|
||||
["F6", "F7", "C7", "D5", "D3" ],
|
||||
["B5", "C6", "B6", "NO_PIN", "NO_PIN"]
|
||||
["F1", "E6", "B0", "B2", "B3"],
|
||||
["F5", "F0", "B1", "B7", "D2"],
|
||||
["F6", "F7", "C7", "D5", "D3"],
|
||||
["B5", "C6", "B6", null, null]
|
||||
]
|
||||
},
|
||||
```
|
||||
|
||||
### Layout macros
|
||||
Here, the matrix dimensions are inferred directly from the dimensions of the `matrix_pins.direct` array. Since there are no row or column pins to prescribe the matrix dimensions, you can arrange it however you like. Each "row" must contain the same number of "column"s; use `null` to fill in blank spaces, but try to minimize them.
|
||||
|
||||
Next is configuring Layout Macro(s). These define the physical arrangement of keys, and its position within the matrix that a switch are connected to. This allows you to have a physical arrangement of keys that differs from the wiring matrix.
|
||||
### Layout Macros
|
||||
|
||||
Next is configuring layout macro(s). These define the physical arrangement of keys, and their position within the matrix that switches are connected to. This allows you to have a physical arrangement of keys that differs from the wiring matrix.
|
||||
|
||||
```json
|
||||
"layouts": {
|
||||
"LAYOUT_ortho_4x4": {
|
||||
"layout": [
|
||||
{ "matrix": [0, 0], "x": 0, "y": 0 },
|
||||
{ "matrix": [0, 1], "x": 1, "y": 0 },
|
||||
{ "matrix": [0, 2], "x": 2, "y": 0 },
|
||||
{ "matrix": [0, 3], "x": 3, "y": 0 },
|
||||
{ "matrix": [1, 0], "x": 0, "y": 1 },
|
||||
{ "matrix": [1, 1], "x": 1, "y": 1 },
|
||||
{ "matrix": [1, 2], "x": 2, "y": 1 },
|
||||
{ "matrix": [1, 3], "x": 3, "y": 1 },
|
||||
{ "matrix": [2, 0], "x": 0, "y": 2 },
|
||||
{ "matrix": [2, 1], "x": 1, "y": 2 },
|
||||
{ "matrix": [2, 2], "x": 2, "y": 2 },
|
||||
{ "matrix": [2, 3], "x": 3, "y": 2 },
|
||||
{ "matrix": [3, 0], "x": 0, "y": 3 },
|
||||
{ "matrix": [3, 1], "x": 1, "y": 3 },
|
||||
{ "matrix": [3, 2], "x": 2, "y": 3 },
|
||||
{ "matrix": [3, 3], "x": 3, "y": 3 }
|
||||
{"matrix": [0, 0], "x": 0, "y": 0},
|
||||
{"matrix": [0, 1], "x": 1, "y": 0},
|
||||
{"matrix": [0, 2], "x": 2, "y": 0},
|
||||
{"matrix": [0, 3], "x": 3, "y": 0},
|
||||
{"matrix": [1, 0], "x": 0, "y": 1},
|
||||
{"matrix": [1, 1], "x": 1, "y": 1},
|
||||
{"matrix": [1, 2], "x": 2, "y": 1},
|
||||
{"matrix": [1, 3], "x": 3, "y": 1},
|
||||
{"matrix": [2, 0], "x": 0, "y": 2},
|
||||
{"matrix": [2, 1], "x": 1, "y": 2},
|
||||
{"matrix": [2, 2], "x": 2, "y": 2},
|
||||
{"matrix": [2, 3], "x": 3, "y": 2},
|
||||
{"matrix": [3, 0], "x": 0, "y": 3},
|
||||
{"matrix": [3, 1], "x": 1, "y": 3},
|
||||
{"matrix": [3, 2], "x": 2, "y": 3},
|
||||
{"matrix": [3, 3], "x": 3, "y": 3}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
In the above example,
|
||||
In the above example,
|
||||
|
||||
* `LAYOUT_ortho_4x4` defines the name of the layout macro
|
||||
* It must conform to the [layout guidelines](hardware_keyboard_guidelines#keyboard-name-h)
|
||||
* `"matrix": [0, 0]` defines the electrical position
|
||||
* `"matrix": [0, 0]` defines the matrix row and column that the key is associated with
|
||||
|
||||
::: tip
|
||||
See also: [Split Keyboard Layout Macro](features/split_keyboard#layout-macro) and [Matrix to Physical Layout](understanding_qmk#matrix-to-physical-layout-map).
|
||||
@ -158,9 +160,10 @@ See also: [Split Keyboard Layout Macro](features/split_keyboard#layout-macro) an
|
||||
|
||||
## Additional Configuration
|
||||
|
||||
There are a lot of features that can be turned on or off, configured or tuned. Some of these have yet to be migrated over to [Data Driven Configuration](data_driven_config). The following sections cover the process for when an `info.json` option is unavailable.
|
||||
There are a lot of features that can be turned on or off, configured or tuned. Some of these have yet to be migrated over to [Data Driven Configuration](data_driven_config). The following sections cover the process for when a data-driven option is unavailable.
|
||||
|
||||
### Configuration Options
|
||||
|
||||
For available options for `config.h`, you should see the [Config Options](config_options#the-configh-file) page for more details.
|
||||
|
||||
### Build Options
|
||||
|
10
keyboards/handwired/onekey/at_start_f415/board.h
Normal file
10
keyboards/handwired/onekey/at_start_f415/board.h
Normal file
@ -0,0 +1,10 @@
|
||||
// Copyright 2023-2024 HorrorTroll <https://github.com/HorrorTroll>
|
||||
// Copyright 2023-2024 Zhaqian <https://github.com/zhaqian12>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include_next <board.h>
|
||||
|
||||
#undef AT32F415KB
|
||||
#define AT32F415RC
|
10
keyboards/handwired/onekey/at_start_f415/config.h
Normal file
10
keyboards/handwired/onekey/at_start_f415/config.h
Normal file
@ -0,0 +1,10 @@
|
||||
// Copyright 2023-2024 HorrorTroll <https://github.com/HorrorTroll>
|
||||
// Copyright 2023-2024 Zhaqian <https://github.com/zhaqian12>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#define ADC_PIN A0
|
||||
|
||||
#define BACKLIGHT_PWM_DRIVER PWMD5
|
||||
#define BACKLIGHT_PWM_CHANNEL 1
|
13
keyboards/handwired/onekey/at_start_f415/halconf.h
Normal file
13
keyboards/handwired/onekey/at_start_f415/halconf.h
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2023-2024 HorrorTroll <https://github.com/HorrorTroll>
|
||||
// Copyright 2023-2024 Zhaqian <https://github.com/zhaqian12>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#define HAL_USE_ADC TRUE
|
||||
|
||||
#define HAL_USE_I2C TRUE
|
||||
|
||||
#define HAL_USE_PWM TRUE
|
||||
|
||||
#include_next <halconf.h>
|
20
keyboards/handwired/onekey/at_start_f415/keyboard.json
Normal file
20
keyboards/handwired/onekey/at_start_f415/keyboard.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"keyboard_name": "Onekey AT-START-F415",
|
||||
"processor": "AT32F415",
|
||||
"bootloader": "at32-dfu",
|
||||
"usb": {
|
||||
"shared_endpoint": {
|
||||
"keyboard": true
|
||||
}
|
||||
},
|
||||
"matrix_pins": {
|
||||
"cols": ["B3"],
|
||||
"rows": ["B4"]
|
||||
},
|
||||
"backlight": {
|
||||
"pin": "A0"
|
||||
},
|
||||
"ws2812": {
|
||||
"pin": "B0"
|
||||
}
|
||||
}
|
16
keyboards/handwired/onekey/at_start_f415/mcuconf.h
Normal file
16
keyboards/handwired/onekey/at_start_f415/mcuconf.h
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2023-2024 HorrorTroll <https://github.com/HorrorTroll>
|
||||
// Copyright 2023-2024 Zhaqian <https://github.com/zhaqian12>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include_next <mcuconf.h>
|
||||
|
||||
#undef AT32_ADC_USE_ADC1
|
||||
#define AT32_ADC_USE_ADC1 TRUE
|
||||
|
||||
#undef AT32_I2C_USE_I2C1
|
||||
#define AT32_I2C_USE_I2C1 TRUE
|
||||
|
||||
#undef AT32_PWM_USE_TMR5
|
||||
#define AT32_PWM_USE_TMR5 TRUE
|
3
keyboards/handwired/onekey/at_start_f415/readme.md
Normal file
3
keyboards/handwired/onekey/at_start_f415/readme.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Artery AT-START-F415 Board Onekey
|
||||
|
||||
To trigger keypress, short together pins *B3* and *B4*.
|
73
keyboards/yowkees/keyball39/config.h
Normal file
73
keyboards/yowkees/keyball39/config.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright 2022 @Yowkees
|
||||
Copyright 2022 MURAOKA Taro (aka KoRoN, @kaoriya)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Key matrix parameters
|
||||
#define MATRIX_MASKED
|
||||
|
||||
// Split parameters
|
||||
#define SPLIT_HAND_MATRIX_GRID_LOW_IS_LEFT
|
||||
#ifdef OLED_ENABLE
|
||||
# define SPLIT_OLED_ENABLE
|
||||
#endif
|
||||
|
||||
// If your PC does not recognize Keyball, try setting this macro. This macro
|
||||
// increases the firmware size by 200 bytes, so it is disabled by default, but
|
||||
// it has been reported to work well in such cases.
|
||||
//#define SPLIT_WATCHDOG_ENABLE
|
||||
|
||||
// PMW3360 configuration
|
||||
#define PMW33XX_CS_PIN B6
|
||||
|
||||
#define SPLIT_TRANSACTION_IDS_KB KEYBALL_GET_INFO, KEYBALL_GET_MOTION, KEYBALL_SET_CPI
|
||||
|
||||
// RGB LED settings
|
||||
#ifdef RGBLIGHT_ENABLE
|
||||
# define RGBLED_NUM 48
|
||||
# define RGBLED_SPLIT { 24, 24 } // (24 + 22)
|
||||
# ifndef RGBLIGHT_LIMIT_VAL
|
||||
# define RGBLIGHT_LIMIT_VAL 150 // limitated for power consumption
|
||||
# endif
|
||||
# ifndef RGBLIGHT_VAL_STEP
|
||||
# define RGBLIGHT_VAL_STEP 15
|
||||
# endif
|
||||
# ifndef RGBLIGHT_HUE_STEP
|
||||
# define RGBLIGHT_HUE_STEP 17
|
||||
# endif
|
||||
# ifndef RGBLIGHT_SAT_STEP
|
||||
# define RGBLIGHT_SAT_STEP 17
|
||||
# endif
|
||||
#endif
|
||||
#ifdef RGB_MATRIX_ENABLE
|
||||
# define RGB_MATRIX_SPLIT { 24, 24 }
|
||||
#endif
|
||||
|
||||
#ifndef OLED_FONT_H
|
||||
# define OLED_FONT_H "../lib/logofont/logofont.c"
|
||||
# define OLED_FONT_START 32
|
||||
# define OLED_FONT_END 195
|
||||
#endif
|
||||
|
||||
#if !defined(LAYER_STATE_8BIT) && !defined(LAYER_STATE_16BIT) && !defined(LAYER_STATE_32BIT)
|
||||
# define LAYER_STATE_8BIT
|
||||
#endif
|
||||
|
||||
// To squeeze firmware size
|
||||
#undef LOCKING_SUPPORT_ENABLE
|
||||
#undef LOCKING_RESYNC_ENABLE
|
46
keyboards/yowkees/keyball39/keyball39.c
Normal file
46
keyboards/yowkees/keyball39/keyball39.c
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright 2021 @Yowkees
|
||||
Copyright 2022 MURAOKA Taro (aka KoRoN, @kaoriya)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include QMK_KEYBOARD_H
|
||||
|
||||
#include "lib/keyball/keyball.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// clang-format off
|
||||
matrix_row_t matrix_mask[MATRIX_ROWS] = {
|
||||
0b00011111,
|
||||
0b00011111,
|
||||
0b00011111,
|
||||
0b00111111,
|
||||
0b00011111,
|
||||
0b00011111,
|
||||
0b00011111,
|
||||
0b00111111,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
void keyball_on_adjust_layout(keyball_adjust_t v) {
|
||||
#ifdef RGBLIGHT_ENABLE
|
||||
// adjust RGBLIGHT's clipping and effect ranges
|
||||
uint8_t lednum_this = keyball.this_have_ball ? 22 : 24;
|
||||
uint8_t lednum_that = !keyball.that_enable ? 0 : keyball.that_have_ball ? 22 : 24;
|
||||
rgblight_set_clipping_range(is_keyboard_left() ? 0 : lednum_that, lednum_this);
|
||||
rgblight_set_effect_range(0, lednum_this + lednum_that);
|
||||
#endif
|
||||
}
|
22
keyboards/yowkees/keyball39/keyball39.h
Normal file
22
keyboards/yowkees/keyball39/keyball39.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
Copyright 2022 @Yowkees
|
||||
Copyright 2022 MURAOKA Taro (aka KoRoN, @kaoriya)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "quantum.h"
|
||||
#include "lib/keyball/keyball.h"
|
239
keyboards/yowkees/keyball39/keyboard.json
Normal file
239
keyboards/yowkees/keyball39/keyboard.json
Normal file
@ -0,0 +1,239 @@
|
||||
{
|
||||
"keyboard_name": "Keyball39",
|
||||
"manufacturer": "Yowkees",
|
||||
"url": "https://github.com/Yowkees/keyball",
|
||||
"maintainer": "qmk",
|
||||
"usb": {
|
||||
"vid": "0x5957",
|
||||
"pid": "0x0200",
|
||||
"device_version": "1.0.0"
|
||||
},
|
||||
"development_board": "promicro",
|
||||
"matrix_pins": {
|
||||
"rows": ["F4", "F5", "F6", "F7"],
|
||||
"cols": ["D4", "C6", "D7", "E6", "B4", "B5"]
|
||||
},
|
||||
"diode_direction": "COL2ROW",
|
||||
"debounce": 5,
|
||||
"features": {
|
||||
"bootmagic": false,
|
||||
"mousekey": false,
|
||||
"extrakey": false,
|
||||
"console": false,
|
||||
"command": false,
|
||||
"nkro": false,
|
||||
"backlight": false,
|
||||
"rgblight": true,
|
||||
"rgb_matrix": false,
|
||||
"oled": true,
|
||||
"pointing_device": true,
|
||||
"audio": false,
|
||||
"sleep_led": false,
|
||||
"grave_esc": false,
|
||||
"magic": false,
|
||||
"space_cadet": false
|
||||
},
|
||||
"build": {
|
||||
"lto": true
|
||||
},
|
||||
"split": {
|
||||
"enabled": true,
|
||||
"soft_serial_pin": "D2",
|
||||
"transport": {
|
||||
"protocol": "serial"
|
||||
},
|
||||
"usb_detect": {
|
||||
"enabled": true
|
||||
},
|
||||
"handedness": {
|
||||
"matrix_grid": ["F6", "B5"]
|
||||
}
|
||||
},
|
||||
"rgblight": {
|
||||
"driver": "ws2812"
|
||||
},
|
||||
"rgb_matrix": {
|
||||
"driver": "ws2812"
|
||||
},
|
||||
"ws2812": {
|
||||
"pin": "D3"
|
||||
},
|
||||
"layout_aliases": {
|
||||
"LAYOUT": "LAYOUT_right_ball",
|
||||
"LAYOUT_universal": "LAYOUT_no_ball"
|
||||
},
|
||||
"layouts": {
|
||||
"LAYOUT_no_ball": {
|
||||
"layout": [
|
||||
{"matrix": [0, 0], "x": 0, "y": 0.6},
|
||||
{"matrix": [0, 1], "x": 1, "y": 0.25},
|
||||
{"matrix": [0, 2], "x": 2, "y": 0},
|
||||
{"matrix": [0, 3], "x": 3, "y": 0.125},
|
||||
{"matrix": [0, 4], "x": 4, "y": 0.25},
|
||||
{"matrix": [4, 4], "x": 9, "y": 0.25},
|
||||
{"matrix": [4, 3], "x": 10, "y": 0.125},
|
||||
{"matrix": [4, 2], "x": 11, "y": 0},
|
||||
{"matrix": [4, 1], "x": 12, "y": 0.25},
|
||||
{"matrix": [4, 0], "x": 13, "y": 0.6},
|
||||
{"matrix": [1, 0], "x": 0, "y": 1.6},
|
||||
{"matrix": [1, 1], "x": 1, "y": 1.25},
|
||||
{"matrix": [1, 2], "x": 2, "y": 1.0},
|
||||
{"matrix": [1, 3], "x": 3, "y": 1.125},
|
||||
{"matrix": [1, 4], "x": 4, "y": 1.25},
|
||||
{"matrix": [5, 4], "x": 9, "y": 1.25},
|
||||
{"matrix": [5, 3], "x": 10, "y": 1.125},
|
||||
{"matrix": [5, 2], "x": 11, "y": 1.0},
|
||||
{"matrix": [5, 1], "x": 12, "y": 1.25},
|
||||
{"matrix": [5, 0], "x": 13, "y": 1.6},
|
||||
{"matrix": [2, 0], "x": 0, "y": 2.6},
|
||||
{"matrix": [2, 1], "x": 1, "y": 2.25},
|
||||
{"matrix": [2, 2], "x": 2, "y": 2.0},
|
||||
{"matrix": [2, 3], "x": 3, "y": 2.125},
|
||||
{"matrix": [2, 4], "x": 4, "y": 2.25},
|
||||
{"matrix": [6, 4], "x": 9, "y": 2.25},
|
||||
{"matrix": [6, 3], "x": 10, "y": 2.125},
|
||||
{"matrix": [6, 2], "x": 11, "y": 2.0},
|
||||
{"matrix": [6, 1], "x": 12, "y": 2.25},
|
||||
{"matrix": [6, 0], "x": 13, "y": 2.6},
|
||||
{"matrix": [3, 0], "x": 0, "y": 3.6},
|
||||
{"matrix": [3, 1], "x": 1, "y": 3.25},
|
||||
{"matrix": [3, 2], "x": 2, "y": 3.0},
|
||||
{"matrix": [3, 3], "x": 3.15, "y": 3.5},
|
||||
{"matrix": [3, 4], "x": 4.25, "y": 3.61},
|
||||
{"matrix": [3, 5], "x": 5.38, "y": 3.86},
|
||||
{"matrix": [7, 5], "x": 7.62, "y": 3.86},
|
||||
{"matrix": [7, 4], "x": 8.75, "y": 3.61},
|
||||
{"matrix": [7, 3], "x": 9.85, "y": 3.5},
|
||||
{"matrix": [7, 2], "x": 11, "y": 3.0},
|
||||
{"matrix": [7, 1], "x": 12, "y": 3.25},
|
||||
{"matrix": [7, 0], "x": 13, "y": 3.6}
|
||||
]
|
||||
},
|
||||
"LAYOUT_left_ball": {
|
||||
"layout": [
|
||||
{"matrix": [0, 0], "x": 0, "y": 0.6},
|
||||
{"matrix": [0, 1], "x": 1, "y": 0.25},
|
||||
{"matrix": [0, 2], "x": 2, "y": 0},
|
||||
{"matrix": [0, 3], "x": 3, "y": 0.125},
|
||||
{"matrix": [0, 4], "x": 4, "y": 0.25},
|
||||
{"matrix": [4, 4], "x": 9, "y": 0.25},
|
||||
{"matrix": [4, 3], "x": 10, "y": 0.125},
|
||||
{"matrix": [4, 2], "x": 11, "y": 0},
|
||||
{"matrix": [4, 1], "x": 12, "y": 0.25},
|
||||
{"matrix": [4, 0], "x": 13, "y": 0.6},
|
||||
{"matrix": [1, 0], "x": 0, "y": 1.6},
|
||||
{"matrix": [1, 1], "x": 1, "y": 1.25},
|
||||
{"matrix": [1, 2], "x": 2, "y": 1.0},
|
||||
{"matrix": [1, 3], "x": 3, "y": 1.125},
|
||||
{"matrix": [1, 4], "x": 4, "y": 1.25},
|
||||
{"matrix": [5, 4], "x": 9, "y": 1.25},
|
||||
{"matrix": [5, 3], "x": 10, "y": 1.125},
|
||||
{"matrix": [5, 2], "x": 11, "y": 1.0},
|
||||
{"matrix": [5, 1], "x": 12, "y": 1.25},
|
||||
{"matrix": [5, 0], "x": 13, "y": 1.6},
|
||||
{"matrix": [2, 0], "x": 0, "y": 2.6},
|
||||
{"matrix": [2, 1], "x": 1, "y": 2.25},
|
||||
{"matrix": [2, 2], "x": 2, "y": 2.0},
|
||||
{"matrix": [2, 3], "x": 3, "y": 2.125},
|
||||
{"matrix": [2, 4], "x": 4, "y": 2.25},
|
||||
{"matrix": [6, 4], "x": 9, "y": 2.25},
|
||||
{"matrix": [6, 3], "x": 10, "y": 2.125},
|
||||
{"matrix": [6, 2], "x": 11, "y": 2.0},
|
||||
{"matrix": [6, 1], "x": 12, "y": 2.25},
|
||||
{"matrix": [6, 0], "x": 13, "y": 2.6},
|
||||
{"matrix": [3, 0], "x": 0, "y": 3.6},
|
||||
{"matrix": [3, 4], "x": 4.25, "y": 3.61},
|
||||
{"matrix": [3, 5], "x": 5.38, "y": 3.86},
|
||||
{"matrix": [7, 5], "x": 7.62, "y": 3.86},
|
||||
{"matrix": [7, 4], "x": 8.75, "y": 3.61},
|
||||
{"matrix": [7, 3], "x": 9.85, "y": 3.5},
|
||||
{"matrix": [7, 2], "x": 11, "y": 3.0},
|
||||
{"matrix": [7, 1], "x": 12, "y": 3.25},
|
||||
{"matrix": [7, 0], "x": 13, "y": 3.6}
|
||||
]
|
||||
},
|
||||
"LAYOUT_right_ball": {
|
||||
"layout": [
|
||||
{"matrix": [0, 0], "x": 0, "y": 0.6},
|
||||
{"matrix": [0, 1], "x": 1, "y": 0.25},
|
||||
{"matrix": [0, 2], "x": 2, "y": 0},
|
||||
{"matrix": [0, 3], "x": 3, "y": 0.125},
|
||||
{"matrix": [0, 4], "x": 4, "y": 0.25},
|
||||
{"matrix": [4, 4], "x": 9, "y": 0.25},
|
||||
{"matrix": [4, 3], "x": 10, "y": 0.125},
|
||||
{"matrix": [4, 2], "x": 11, "y": 0},
|
||||
{"matrix": [4, 1], "x": 12, "y": 0.25},
|
||||
{"matrix": [4, 0], "x": 13, "y": 0.6},
|
||||
{"matrix": [1, 0], "x": 0, "y": 1.6},
|
||||
{"matrix": [1, 1], "x": 1, "y": 1.25},
|
||||
{"matrix": [1, 2], "x": 2, "y": 1.0},
|
||||
{"matrix": [1, 3], "x": 3, "y": 1.125},
|
||||
{"matrix": [1, 4], "x": 4, "y": 1.25},
|
||||
{"matrix": [5, 4], "x": 9, "y": 1.25},
|
||||
{"matrix": [5, 3], "x": 10, "y": 1.125},
|
||||
{"matrix": [5, 2], "x": 11, "y": 1.0},
|
||||
{"matrix": [5, 1], "x": 12, "y": 1.25},
|
||||
{"matrix": [5, 0], "x": 13, "y": 1.6},
|
||||
{"matrix": [2, 0], "x": 0, "y": 2.6},
|
||||
{"matrix": [2, 1], "x": 1, "y": 2.25},
|
||||
{"matrix": [2, 2], "x": 2, "y": 2.0},
|
||||
{"matrix": [2, 3], "x": 3, "y": 2.125},
|
||||
{"matrix": [2, 4], "x": 4, "y": 2.25},
|
||||
{"matrix": [6, 4], "x": 9, "y": 2.25},
|
||||
{"matrix": [6, 3], "x": 10, "y": 2.125},
|
||||
{"matrix": [6, 2], "x": 11, "y": 2.0},
|
||||
{"matrix": [6, 1], "x": 12, "y": 2.25},
|
||||
{"matrix": [6, 0], "x": 13, "y": 2.6},
|
||||
{"matrix": [3, 0], "x": 0, "y": 3.6},
|
||||
{"matrix": [3, 1], "x": 1, "y": 3.25},
|
||||
{"matrix": [3, 2], "x": 2, "y": 3.0},
|
||||
{"matrix": [3, 3], "x": 3.15, "y": 3.5},
|
||||
{"matrix": [3, 4], "x": 4.25, "y": 3.61},
|
||||
{"matrix": [3, 5], "x": 5.38, "y": 3.86},
|
||||
{"matrix": [7, 5], "x": 7.62, "y": 3.86},
|
||||
{"matrix": [7, 4], "x": 8.75, "y": 3.61},
|
||||
{"matrix": [7, 0], "x": 13, "y": 3.6}
|
||||
]
|
||||
},
|
||||
"LAYOUT_dual_ball": {
|
||||
"layout": [
|
||||
{"matrix": [0, 0], "x": 0, "y": 0.6},
|
||||
{"matrix": [0, 1], "x": 1, "y": 0.25},
|
||||
{"matrix": [0, 2], "x": 2, "y": 0},
|
||||
{"matrix": [0, 3], "x": 3, "y": 0.125},
|
||||
{"matrix": [0, 4], "x": 4, "y": 0.25},
|
||||
{"matrix": [4, 4], "x": 9, "y": 0.25},
|
||||
{"matrix": [4, 3], "x": 10, "y": 0.125},
|
||||
{"matrix": [4, 2], "x": 11, "y": 0},
|
||||
{"matrix": [4, 1], "x": 12, "y": 0.25},
|
||||
{"matrix": [4, 0], "x": 13, "y": 0.6},
|
||||
{"matrix": [1, 0], "x": 0, "y": 1.6},
|
||||
{"matrix": [1, 1], "x": 1, "y": 1.25},
|
||||
{"matrix": [1, 2], "x": 2, "y": 1.0},
|
||||
{"matrix": [1, 3], "x": 3, "y": 1.125},
|
||||
{"matrix": [1, 4], "x": 4, "y": 1.25},
|
||||
{"matrix": [5, 4], "x": 9, "y": 1.25},
|
||||
{"matrix": [5, 3], "x": 10, "y": 1.125},
|
||||
{"matrix": [5, 2], "x": 11, "y": 1.0},
|
||||
{"matrix": [5, 1], "x": 12, "y": 1.25},
|
||||
{"matrix": [5, 0], "x": 13, "y": 1.6},
|
||||
{"matrix": [2, 0], "x": 0, "y": 2.6},
|
||||
{"matrix": [2, 1], "x": 1, "y": 2.25},
|
||||
{"matrix": [2, 2], "x": 2, "y": 2.0},
|
||||
{"matrix": [2, 3], "x": 3, "y": 2.125},
|
||||
{"matrix": [2, 4], "x": 4, "y": 2.25},
|
||||
{"matrix": [6, 4], "x": 9, "y": 2.25},
|
||||
{"matrix": [6, 3], "x": 10, "y": 2.125},
|
||||
{"matrix": [6, 2], "x": 11, "y": 2.0},
|
||||
{"matrix": [6, 1], "x": 12, "y": 2.25},
|
||||
{"matrix": [6, 0], "x": 13, "y": 2.6},
|
||||
{"matrix": [3, 0], "x": 0, "y": 3.6},
|
||||
{"matrix": [3, 4], "x": 4.25, "y": 3.61},
|
||||
{"matrix": [3, 5], "x": 5.38, "y": 3.86},
|
||||
{"matrix": [7, 5], "x": 7.62, "y": 3.86},
|
||||
{"matrix": [7, 4], "x": 8.75, "y": 3.61},
|
||||
{"matrix": [7, 0], "x": 13, "y": 3.6}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
41
keyboards/yowkees/keyball39/keymaps/default/config.h
Normal file
41
keyboards/yowkees/keyball39/keymaps/default/config.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
This is the c configuration file for the keymap
|
||||
|
||||
Copyright 2022 @Yowkees
|
||||
Copyright 2022 MURAOKA Taro (aka KoRoN, @kaoriya)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define PMW33XX_UPLOAD_SROM
|
||||
|
||||
#ifdef RGBLIGHT_ENABLE
|
||||
//# define RGBLIGHT_EFFECT_BREATHING
|
||||
//# define RGBLIGHT_EFFECT_RAINBOW_MOOD
|
||||
//# define RGBLIGHT_EFFECT_RAINBOW_SWIRL
|
||||
//# define RGBLIGHT_EFFECT_SNAKE
|
||||
//# define RGBLIGHT_EFFECT_KNIGHT
|
||||
//# define RGBLIGHT_EFFECT_CHRISTMAS
|
||||
//# define RGBLIGHT_EFFECT_STATIC_GRADIENT
|
||||
//# define RGBLIGHT_EFFECT_RGB_TEST
|
||||
//# define RGBLIGHT_EFFECT_ALTERNATING
|
||||
//# define RGBLIGHT_EFFECT_TWINKLE
|
||||
#endif
|
||||
|
||||
#define TAP_CODE_DELAY 5
|
||||
|
||||
#define POINTING_DEVICE_AUTO_MOUSE_ENABLE
|
||||
#define AUTO_MOUSE_DEFAULT_LAYER 1
|
69
keyboards/yowkees/keyball39/keymaps/default/keymap.c
Normal file
69
keyboards/yowkees/keyball39/keymaps/default/keymap.c
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
Copyright 2022 @Yowkees
|
||||
Copyright 2022 MURAOKA Taro (aka KoRoN, @kaoriya)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include QMK_KEYBOARD_H
|
||||
|
||||
// clang-format off
|
||||
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
|
||||
// keymap for default
|
||||
[0] = LAYOUT_no_ball(
|
||||
KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P,
|
||||
KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_MINS,
|
||||
KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH,
|
||||
KC_LCTL, KC_LGUI, KC_LALT, LSFT_T(KC_LNG2), LT(1,KC_SPC), LT(3,KC_LNG1), KC_BSPC, LT(2,KC_ENT), LSFT_T(KC_LNG2), KC_RALT, KC_RGUI, KC_RSFT
|
||||
),
|
||||
|
||||
[1] = LAYOUT_no_ball(
|
||||
KC_F1, KC_F2, KC_F3, KC_F4, KC_RBRC, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10,
|
||||
KC_F5, KC_EXLM, S(KC_6), S(KC_INT3), S(KC_8), S(KC_INT1), KC_BTN1, KC_PGUP, KC_BTN2, KC_SCLN,
|
||||
S(KC_EQL), S(KC_LBRC), S(KC_7), S(KC_2), S(KC_RBRC), KC_LBRC, KC_DLR, KC_PGDN, KC_BTN3, KC_F11,
|
||||
KC_INT1, KC_EQL, S(KC_3), _______, _______, _______, TO(2), TO(0), _______, KC_RALT, KC_RGUI, KC_F12
|
||||
),
|
||||
|
||||
[2] = LAYOUT_no_ball(
|
||||
KC_TAB, KC_7, KC_8, KC_9, KC_MINS, KC_NUHS, _______, KC_BTN3, _______, KC_BSPC,
|
||||
S(KC_QUOT), KC_4, KC_5, KC_6, S(KC_SCLN), S(KC_9), KC_BTN1, KC_UP, KC_BTN2, KC_QUOT,
|
||||
KC_SLSH, KC_1, KC_2, KC_3, S(KC_MINS), S(KC_NUHS), KC_LEFT, KC_DOWN, KC_RGHT, _______,
|
||||
KC_ESC, KC_0, KC_DOT, KC_DEL, KC_ENT, KC_BSPC, _______, _______, _______, _______, _______, _______
|
||||
),
|
||||
|
||||
[3] = LAYOUT_no_ball(
|
||||
RGB_TOG, AML_TO, AML_I50, AML_D50, _______, _______, _______, SSNP_HOR, SSNP_VRT, SSNP_FRE,
|
||||
RGB_MOD, RGB_HUI, RGB_SAI, RGB_VAI, SCRL_DVI, _______, _______, _______, _______, _______,
|
||||
RGB_RMOD, RGB_HUD, RGB_SAD, RGB_VAD, SCRL_DVD, CPI_D1K, CPI_D100, CPI_I100, CPI_I1K, KBC_SAVE,
|
||||
QK_BOOT, KBC_RST, _______, _______, _______, _______, _______, _______, _______, _______, KBC_RST, QK_BOOT
|
||||
),
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
layer_state_t layer_state_set_user(layer_state_t state) {
|
||||
// Auto enable scroll mode when the highest layer is 3
|
||||
keyball_set_scroll_mode(get_highest_layer(state) == 3);
|
||||
return state;
|
||||
}
|
||||
|
||||
#ifdef OLED_ENABLE
|
||||
|
||||
# include "lib/oledkit/oledkit.h"
|
||||
|
||||
void oledkit_render_info_user(void) {
|
||||
keyball_oled_render_keyinfo();
|
||||
keyball_oled_render_ballinfo();
|
||||
keyball_oled_render_layerinfo();
|
||||
}
|
||||
#endif
|
33
keyboards/yowkees/keyball39/keymaps/develop/config.h
Normal file
33
keyboards/yowkees/keyball39/keymaps/develop/config.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
This is the c configuration file for the keymap
|
||||
|
||||
Copyright 2022 MURAOKA Taro (aka KoRoN, @kaoriya)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef RGBLIGHT_ENABLE
|
||||
# define RGBLIGHT_EFFECT_BREATHING
|
||||
# define RGBLIGHT_EFFECT_RAINBOW_MOOD
|
||||
# define RGBLIGHT_EFFECT_RAINBOW_SWIRL
|
||||
# define RGBLIGHT_EFFECT_SNAKE
|
||||
# define RGBLIGHT_EFFECT_KNIGHT
|
||||
# define RGBLIGHT_EFFECT_CHRISTMAS
|
||||
# define RGBLIGHT_EFFECT_STATIC_GRADIENT
|
||||
# define RGBLIGHT_EFFECT_RGB_TEST
|
||||
# define RGBLIGHT_EFFECT_ALTERNATING
|
||||
# define RGBLIGHT_EFFECT_TWINKLE
|
||||
#endif
|
49
keyboards/yowkees/keyball39/keymaps/develop/keymap.c
Normal file
49
keyboards/yowkees/keyball39/keymaps/develop/keymap.c
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright 2022 @Yowkees
|
||||
Copyright 2022 MURAOKA Taro (aka KoRoN, @kaoriya)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include QMK_KEYBOARD_H
|
||||
|
||||
// clang-format off
|
||||
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
|
||||
// keymap for development
|
||||
[0] = LAYOUT_no_ball(
|
||||
KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P,
|
||||
KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN,
|
||||
KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH,
|
||||
KC_LCTL, KC_LGUI, KC_LALT, SCRL_MO, KC_SPC, LT(1,KC_TAB), LT(1,KC_BSPC), KC_ENT, SCRL_MO, KC_RALT, KC_RGUI, KC_RSFT
|
||||
),
|
||||
|
||||
[1] = LAYOUT_no_ball(
|
||||
RGB_TOG, _______, _______, _______, _______, RGB_M_P, RGB_M_B, RGB_M_R, RGB_M_SW, RGB_M_SN,
|
||||
RGB_MOD, RGB_HUI, RGB_SAI, RGB_VAI, SCRL_DVI, RGB_M_K, RGB_M_X, RGB_M_G, RGB_M_T, RGB_M_TW,
|
||||
RGB_RMOD, RGB_HUD, RGB_SAD, RGB_VAD, SCRL_DVD, CPI_D1K, CPI_D100, CPI_I100, CPI_I1K, KBC_SAVE,
|
||||
QK_BOOT, KBC_RST, _______, _______, _______, _______, _______, _______, _______, _______, KBC_RST, QK_BOOT
|
||||
),
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
#ifdef OLED_ENABLE
|
||||
|
||||
# include "lib/oledkit/oledkit.h"
|
||||
|
||||
void oledkit_render_info_user(void) {
|
||||
keyball_oled_render_keyinfo();
|
||||
keyball_oled_render_ballinfo();
|
||||
keyball_oled_render_layerinfo();
|
||||
}
|
||||
#endif
|
2
keyboards/yowkees/keyball39/keymaps/develop/rules.mk
Normal file
2
keyboards/yowkees/keyball39/keymaps/develop/rules.mk
Normal file
@ -0,0 +1,2 @@
|
||||
# for debug
|
||||
#CONSOLE_ENABLE = yes
|
25
keyboards/yowkees/keyball39/keymaps/test/config.h
Normal file
25
keyboards/yowkees/keyball39/keymaps/test/config.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
This is the c configuration file for the keymap
|
||||
|
||||
Copyright 2022 @Yowkees
|
||||
Copyright 2022 MURAOKA Taro (aka KoRoN, @kaoriya)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef RGBLIGHT_ENABLE
|
||||
# define RGBLIGHT_EFFECT_RGB_TEST // required for LED test
|
||||
#endif
|
49
keyboards/yowkees/keyball39/keymaps/test/keymap.c
Normal file
49
keyboards/yowkees/keyball39/keymaps/test/keymap.c
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright 2022 @Yowkees
|
||||
Copyright 2022 MURAOKA Taro (aka KoRoN, @kaoriya)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include QMK_KEYBOARD_H
|
||||
|
||||
// clang-format off
|
||||
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
|
||||
[0] = LAYOUT_no_ball(
|
||||
KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P,
|
||||
KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN,
|
||||
KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH,
|
||||
KC_LCTL, KC_LGUI, KC_LALT, KC_ESC, KC_SPC, KC_TAB, KC_BSPC, KC_ENT, KC_ESC, KC_RALT, KC_RGUI, KC_RSFT
|
||||
),
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
void keyboard_post_init_user() {
|
||||
#ifdef RGBLIGHT_ENABLE
|
||||
// Force RGB lights to show test animation without writing EEPROM.
|
||||
rgblight_enable_noeeprom();
|
||||
rgblight_mode_noeeprom(RGBLIGHT_MODE_RGB_TEST);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef OLED_ENABLE
|
||||
|
||||
# include "lib/oledkit/oledkit.h"
|
||||
|
||||
void oledkit_render_info_user(void) {
|
||||
keyball_oled_render_keyinfo();
|
||||
keyball_oled_render_ballinfo();
|
||||
keyball_oled_render_layerinfo();
|
||||
}
|
||||
#endif
|
2
keyboards/yowkees/keyball39/keymaps/test/rules.mk
Normal file
2
keyboards/yowkees/keyball39/keymaps/test/rules.mk
Normal file
@ -0,0 +1,2 @@
|
||||
# for debug
|
||||
#CONSOLE_ENABLE = yes
|
21
keyboards/yowkees/keyball39/readme.md
Normal file
21
keyboards/yowkees/keyball39/readme.md
Normal file
@ -0,0 +1,21 @@
|
||||
# Keyball39
|
||||
|
||||
![Keyball39](https://raw.githubusercontent.com/Yowkees/keyball/main/keyball39/doc/rev1/images/kb39_001.jpg)
|
||||
|
||||
A split keyboard with 39 vertically staggered keys and 34mm track ball.
|
||||
|
||||
* Keyboard Maintainer: [@Yowkees](https://twitter.com/Yowkees)
|
||||
* Hardware Supported: Keyball39 PCB, ProMicro
|
||||
* Hardware Availability:
|
||||
* <https://shop.yushakobo.jp/products/5357>
|
||||
* <https://shirogane-lab.net/items/64b8f8693ee3fd0045280190>
|
||||
|
||||
Make example for this keyboard (after setting up your build environment):
|
||||
|
||||
make yowkees/keyball39:default
|
||||
|
||||
See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
|
||||
|
||||
## Special keycodes
|
||||
|
||||
See [Special Keycode](../lib/keyball/keycodes.md) file.
|
7
keyboards/yowkees/keyball39/rules.mk
Normal file
7
keyboards/yowkees/keyball39/rules.mk
Normal file
@ -0,0 +1,7 @@
|
||||
# Optical sensor driver for trackball.
|
||||
POINTING_DEVICE_DRIVER = pmw3360
|
||||
|
||||
SRC += lib/oledkit/oledkit.c # OLED utility for Keyball series.
|
||||
|
||||
# Include common library
|
||||
SRC += lib/keyball/keyball.c
|
177
keyboards/yowkees/lib/duplexmatrix/duplexmatrix.c
Normal file
177
keyboards/yowkees/lib/duplexmatrix/duplexmatrix.c
Normal file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
Copyright 2022 MURAOKA Taro (aka KoRoN, @kaoriya)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "quantum.h"
|
||||
#include "matrix.h"
|
||||
#include "debounce.h"
|
||||
|
||||
#ifdef SPLIT_KEYBOARD
|
||||
# include "split_common/split_util.h"
|
||||
# include "split_common/transactions.h"
|
||||
#endif
|
||||
|
||||
#ifdef SPLIT_KEYBOARD
|
||||
# define PINNUM_ROW (MATRIX_ROWS / 2)
|
||||
# define ROWS_PER_HAND (MATRIX_ROWS / 2)
|
||||
#else
|
||||
# define PINNUM_ROW (MATRIX_ROWS)
|
||||
# define ROWS_PER_HAND (MATRIX_ROWS)
|
||||
#endif
|
||||
#define PINNUM_COL (MATRIX_COLS / 2)
|
||||
|
||||
#define MATRIXSIZE_PER_HAND (ROWS_PER_HAND * sizeof(matrix_row_t))
|
||||
|
||||
static pin_t row_pins[PINNUM_ROW] = MATRIX_ROW_PINS;
|
||||
static pin_t col_pins[PINNUM_COL] = MATRIX_COL_PINS;
|
||||
|
||||
static inline void set_pin_input(pin_t pin) {
|
||||
setPinInputHigh(pin);
|
||||
}
|
||||
|
||||
static void set_pins_input(pin_t* pins, uint8_t n) {
|
||||
for (uint8_t i = 0; i < n; i++) {
|
||||
set_pin_input(pins[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void set_pin_output(pin_t pin) {
|
||||
setPinOutput(pin);
|
||||
writePinLow(pin);
|
||||
}
|
||||
|
||||
static inline bool get_pin(pin_t pin) {
|
||||
return readPin(pin);
|
||||
}
|
||||
|
||||
__attribute__((weak)) void duplex_scan_raw_post_kb(matrix_row_t out_matrix[]) {}
|
||||
|
||||
static void duplex_scan_raw(matrix_row_t out_matrix[]) {
|
||||
// scan column to row
|
||||
for (uint8_t row = 0; row < PINNUM_ROW; row++) {
|
||||
set_pin_output(row_pins[row]);
|
||||
matrix_output_select_delay();
|
||||
for (uint8_t col = 0; col < PINNUM_COL; col++) {
|
||||
if (!get_pin(col_pins[col])) {
|
||||
out_matrix[row] |= 1 << col;
|
||||
}
|
||||
}
|
||||
set_pin_input(row_pins[row]);
|
||||
matrix_output_unselect_delay(row, false);
|
||||
}
|
||||
|
||||
// scan row to column.
|
||||
for (uint8_t col = 0; col < PINNUM_COL; col++) {
|
||||
set_pin_output(col_pins[col]);
|
||||
matrix_output_select_delay();
|
||||
matrix_row_t shifter = ((matrix_row_t)1) << (col + PINNUM_COL);
|
||||
for (uint8_t row = 0; row < PINNUM_ROW; row++) {
|
||||
if (!get_pin(row_pins[row])) {
|
||||
out_matrix[row] |= shifter;
|
||||
}
|
||||
}
|
||||
set_pin_input(col_pins[col]);
|
||||
matrix_output_unselect_delay(col, false);
|
||||
}
|
||||
|
||||
duplex_scan_raw_post_kb(out_matrix);
|
||||
}
|
||||
|
||||
static bool duplex_scan(matrix_row_t current_matrix[]) {
|
||||
bool changed = false;
|
||||
matrix_row_t tmp[MATRIX_ROWS] = {0};
|
||||
|
||||
duplex_scan_raw(tmp);
|
||||
for (uint8_t row = 0; row < PINNUM_ROW; row++) {
|
||||
if (tmp[row] != current_matrix[row]) {
|
||||
changed = true;
|
||||
current_matrix[row] = tmp[row];
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
#ifdef SPLIT_KEYBOARD
|
||||
static uint8_t thisHand, thatHand;
|
||||
#else
|
||||
# define thisHand 0
|
||||
#endif
|
||||
|
||||
void matrix_init_custom(void) {
|
||||
#ifdef SPLIT_KEYBOARD
|
||||
split_pre_init();
|
||||
#endif
|
||||
|
||||
set_pins_input(col_pins, PINNUM_COL);
|
||||
set_pins_input(row_pins, PINNUM_ROW);
|
||||
|
||||
#ifdef SPLIT_KEYBOARD
|
||||
thisHand = isLeftHand ? 0 : ROWS_PER_HAND;
|
||||
thatHand = ROWS_PER_HAND - thisHand;
|
||||
|
||||
split_post_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SPLIT_KEYBOARD
|
||||
|
||||
// user-defined overridable functions
|
||||
__attribute__((weak)) void matrix_slave_scan_kb(void) {
|
||||
matrix_slave_scan_user();
|
||||
}
|
||||
|
||||
__attribute__((weak)) void matrix_slave_scan_user(void) {}
|
||||
|
||||
#endif
|
||||
|
||||
// declare matrix buffers which defined in quantum/matrix_common.c
|
||||
extern matrix_row_t raw_matrix[MATRIX_ROWS];
|
||||
extern matrix_row_t matrix[MATRIX_ROWS];
|
||||
|
||||
uint8_t matrix_scan(void) {
|
||||
bool changed = duplex_scan(raw_matrix);
|
||||
|
||||
debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, changed);
|
||||
|
||||
#ifdef SPLIT_KEYBOARD
|
||||
if (!is_keyboard_master()) {
|
||||
// send to primary.
|
||||
transport_slave(matrix + thatHand, matrix + thisHand);
|
||||
matrix_slave_scan_kb();
|
||||
return changed;
|
||||
}
|
||||
|
||||
// receive from secondary.
|
||||
static bool last_connected = false;
|
||||
matrix_row_t* that_raw = raw_matrix + ROWS_PER_HAND;
|
||||
memset(that_raw, 0, MATRIXSIZE_PER_HAND);
|
||||
if (transport_master_if_connected(matrix + thisHand, that_raw)) {
|
||||
last_connected = true;
|
||||
if (memcmp(matrix + thatHand, that_raw, MATRIXSIZE_PER_HAND) != 0) {
|
||||
memcpy(matrix + thatHand, that_raw, MATRIXSIZE_PER_HAND);
|
||||
changed = true;
|
||||
}
|
||||
} else if (last_connected) {
|
||||
last_connected = false;
|
||||
memset(matrix + thatHand, 0, MATRIXSIZE_PER_HAND);
|
||||
changed = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
matrix_scan_kb();
|
||||
return changed;
|
||||
}
|
20
keyboards/yowkees/lib/duplexmatrix/duplexmatrix.h
Normal file
20
keyboards/yowkees/lib/duplexmatrix/duplexmatrix.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright 2022 MURAOKA Taro (aka KoRoN, @kaoriya)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void duplex_scan_raw_post_kb(matrix_row_t out_matrix[]);
|
234
keyboards/yowkees/lib/glcdfont.c
Normal file
234
keyboards/yowkees/lib/glcdfont.c
Normal file
@ -0,0 +1,234 @@
|
||||
// This is the 'classic' fixed-space bitmap font for Adafruit_GFX since 1.0.
|
||||
// See gfxfont.h for newer custom bitmap font info.
|
||||
|
||||
#include "progmem.h"
|
||||
|
||||
// Standard ASCII 5x7 font
|
||||
// clang-format off
|
||||
const unsigned char font[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 0x00,
|
||||
0x3E, 0x6B, 0x4F, 0x6B, 0x3E, 0x00,
|
||||
0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 0x00,
|
||||
0x18, 0x3C, 0x7E, 0x3C, 0x18, 0x00,
|
||||
0x1C, 0x57, 0x7D, 0x57, 0x1C, 0x00,
|
||||
0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 0x00,
|
||||
0x00, 0x18, 0x3C, 0x18, 0x00, 0x00,
|
||||
0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 0x00,
|
||||
0x00, 0x18, 0x24, 0x18, 0x00, 0x00,
|
||||
0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 0x00,
|
||||
0x30, 0x48, 0x3A, 0x06, 0x0E, 0x00,
|
||||
0x26, 0x29, 0x79, 0x29, 0x26, 0x00,
|
||||
0x40, 0x7F, 0x05, 0x05, 0x07, 0x00,
|
||||
0x40, 0x7F, 0x05, 0x25, 0x3F, 0x00,
|
||||
0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 0x00,
|
||||
0x7F, 0x3E, 0x1C, 0x1C, 0x08, 0x00,
|
||||
0x08, 0x1C, 0x1C, 0x3E, 0x7F, 0x00,
|
||||
0x14, 0x22, 0x7F, 0x22, 0x14, 0x00,
|
||||
0x5F, 0x5F, 0x00, 0x5F, 0x5F, 0x00,
|
||||
0x06, 0x09, 0x7F, 0x01, 0x7F, 0x00,
|
||||
0x00, 0x66, 0x89, 0x95, 0x6A, 0x00,
|
||||
0x60, 0x60, 0x60, 0x60, 0x60, 0x00,
|
||||
0x94, 0xA2, 0xFF, 0xA2, 0x94, 0x00,
|
||||
0x08, 0x04, 0x7E, 0x04, 0x08, 0x00,
|
||||
0x10, 0x20, 0x7E, 0x20, 0x10, 0x00,
|
||||
0x08, 0x08, 0x2A, 0x1C, 0x08, 0x00,
|
||||
0x08, 0x1C, 0x2A, 0x08, 0x08, 0x00,
|
||||
0x1E, 0x10, 0x10, 0x10, 0x10, 0x00,
|
||||
0x0C, 0x1E, 0x0C, 0x1E, 0x0C, 0x00,
|
||||
0x30, 0x38, 0x3E, 0x38, 0x30, 0x00,
|
||||
0x06, 0x0E, 0x3E, 0x0E, 0x06, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x5F, 0x00, 0x00, 0x00,
|
||||
0x00, 0x07, 0x00, 0x07, 0x00, 0x00,
|
||||
0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00,
|
||||
0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x00,
|
||||
0x23, 0x13, 0x08, 0x64, 0x62, 0x00,
|
||||
0x36, 0x49, 0x56, 0x20, 0x50, 0x00,
|
||||
0x00, 0x08, 0x07, 0x03, 0x00, 0x00,
|
||||
0x00, 0x1C, 0x22, 0x41, 0x00, 0x00,
|
||||
0x00, 0x41, 0x22, 0x1C, 0x00, 0x00,
|
||||
0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x00,
|
||||
0x08, 0x08, 0x3E, 0x08, 0x08, 0x00,
|
||||
0x00, 0x80, 0x70, 0x30, 0x00, 0x00,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x00,
|
||||
0x00, 0x00, 0x60, 0x60, 0x00, 0x00,
|
||||
0x20, 0x10, 0x08, 0x04, 0x02, 0x00,
|
||||
0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00,
|
||||
0x00, 0x42, 0x7F, 0x40, 0x00, 0x00,
|
||||
0x72, 0x49, 0x49, 0x49, 0x46, 0x00,
|
||||
0x21, 0x41, 0x49, 0x4D, 0x33, 0x00,
|
||||
0x18, 0x14, 0x12, 0x7F, 0x10, 0x00,
|
||||
0x27, 0x45, 0x45, 0x45, 0x39, 0x00,
|
||||
0x3C, 0x4A, 0x49, 0x49, 0x31, 0x00,
|
||||
0x41, 0x21, 0x11, 0x09, 0x07, 0x00,
|
||||
0x36, 0x49, 0x49, 0x49, 0x36, 0x00,
|
||||
0x46, 0x49, 0x49, 0x29, 0x1E, 0x00,
|
||||
0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
|
||||
0x00, 0x40, 0x34, 0x00, 0x00, 0x00,
|
||||
0x00, 0x08, 0x14, 0x22, 0x41, 0x00,
|
||||
0x14, 0x14, 0x14, 0x14, 0x14, 0x00,
|
||||
0x00, 0x41, 0x22, 0x14, 0x08, 0x00,
|
||||
0x02, 0x01, 0x59, 0x09, 0x06, 0x00,
|
||||
0x3E, 0x41, 0x5D, 0x59, 0x4E, 0x00,
|
||||
0x7C, 0x12, 0x11, 0x12, 0x7C, 0x00,
|
||||
0x7F, 0x49, 0x49, 0x49, 0x36, 0x00,
|
||||
0x3E, 0x41, 0x41, 0x41, 0x22, 0x00,
|
||||
0x7F, 0x41, 0x41, 0x41, 0x3E, 0x00,
|
||||
0x7F, 0x49, 0x49, 0x49, 0x41, 0x00,
|
||||
0x7F, 0x09, 0x09, 0x09, 0x01, 0x00,
|
||||
0x3E, 0x41, 0x41, 0x51, 0x73, 0x00,
|
||||
0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00,
|
||||
0x00, 0x41, 0x7F, 0x41, 0x00, 0x00,
|
||||
0x20, 0x40, 0x41, 0x3F, 0x01, 0x00,
|
||||
0x7F, 0x08, 0x14, 0x22, 0x41, 0x00,
|
||||
0x7F, 0x40, 0x40, 0x40, 0x40, 0x00,
|
||||
0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x00,
|
||||
0x7F, 0x04, 0x08, 0x10, 0x7F, 0x00,
|
||||
0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00,
|
||||
0x7F, 0x09, 0x09, 0x09, 0x06, 0x00,
|
||||
0x3E, 0x41, 0x51, 0x21, 0x5E, 0x00,
|
||||
0x7F, 0x09, 0x19, 0x29, 0x46, 0x00,
|
||||
0x26, 0x49, 0x49, 0x49, 0x32, 0x00,
|
||||
0x03, 0x01, 0x7F, 0x01, 0x03, 0x00,
|
||||
0x3F, 0x40, 0x40, 0x40, 0x3F, 0x00,
|
||||
0x1F, 0x20, 0x40, 0x20, 0x1F, 0x00,
|
||||
0x3F, 0x40, 0x38, 0x40, 0x3F, 0x00,
|
||||
0x63, 0x14, 0x08, 0x14, 0x63, 0x00,
|
||||
0x03, 0x04, 0x78, 0x04, 0x03, 0x00,
|
||||
0x61, 0x59, 0x49, 0x4D, 0x43, 0x00,
|
||||
0x00, 0x7F, 0x41, 0x41, 0x41, 0x00,
|
||||
0x02, 0x04, 0x08, 0x10, 0x20, 0x00,
|
||||
0x00, 0x41, 0x41, 0x41, 0x7F, 0x00,
|
||||
0x04, 0x02, 0x01, 0x02, 0x04, 0x00,
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x00,
|
||||
0x00, 0x03, 0x07, 0x08, 0x00, 0x00,
|
||||
0x20, 0x54, 0x54, 0x78, 0x40, 0x00,
|
||||
0x7F, 0x28, 0x44, 0x44, 0x38, 0x00,
|
||||
0x38, 0x44, 0x44, 0x44, 0x28, 0x00,
|
||||
0x38, 0x44, 0x44, 0x28, 0x7F, 0x00,
|
||||
0x38, 0x54, 0x54, 0x54, 0x18, 0x00,
|
||||
0x00, 0x08, 0x7E, 0x09, 0x02, 0x00,
|
||||
0x18, 0x24, 0x24, 0x1C, 0x78, 0x00,
|
||||
0x7F, 0x08, 0x04, 0x04, 0x78, 0x00,
|
||||
0x00, 0x44, 0x7D, 0x40, 0x00, 0x00,
|
||||
0x20, 0x40, 0x40, 0x3D, 0x00, 0x00,
|
||||
0x7F, 0x10, 0x28, 0x44, 0x00, 0x00,
|
||||
0x00, 0x41, 0x7F, 0x40, 0x00, 0x00,
|
||||
0x7C, 0x04, 0x78, 0x04, 0x78, 0x00,
|
||||
0x7C, 0x08, 0x04, 0x04, 0x78, 0x00,
|
||||
0x38, 0x44, 0x44, 0x44, 0x38, 0x00,
|
||||
0x7C, 0x18, 0x24, 0x24, 0x18, 0x00,
|
||||
0x18, 0x24, 0x24, 0x18, 0x7C, 0x00,
|
||||
0x7C, 0x08, 0x04, 0x04, 0x08, 0x00,
|
||||
0x48, 0x54, 0x54, 0x54, 0x24, 0x00,
|
||||
0x04, 0x04, 0x3F, 0x44, 0x24, 0x00,
|
||||
0x3C, 0x40, 0x40, 0x20, 0x7C, 0x00,
|
||||
0x1C, 0x20, 0x40, 0x20, 0x1C, 0x00,
|
||||
0x3C, 0x40, 0x30, 0x40, 0x3C, 0x00,
|
||||
0x44, 0x28, 0x10, 0x28, 0x44, 0x00,
|
||||
0x4C, 0x90, 0x90, 0x90, 0x7C, 0x00,
|
||||
0x44, 0x64, 0x54, 0x4C, 0x44, 0x00,
|
||||
0x00, 0x08, 0x36, 0x41, 0x00, 0x00,
|
||||
0x00, 0x00, 0x77, 0x00, 0x00, 0x00,
|
||||
0x00, 0x41, 0x36, 0x08, 0x00, 0x00,
|
||||
0x02, 0x01, 0x02, 0x04, 0x02, 0x00,
|
||||
0x3C, 0x26, 0x23, 0x26, 0x3C, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xC0, 0xF0, 0xF8, 0x8C, 0x86, 0xC6,
|
||||
0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFE, 0xFC, 0xFC, 0xF8, 0xE0,
|
||||
0x80, 0x00, 0x00, 0x00, 0xF0, 0xF0,
|
||||
0xF0, 0x00, 0x00, 0xC0, 0xE0, 0xF0,
|
||||
0x70, 0x70, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xF8, 0xF8, 0xF8, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xF8, 0xF8,
|
||||
0xF8, 0x00, 0x00, 0xF8, 0xF8, 0xF8,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xE0, 0xF0, 0xF0, 0xF0, 0xE0, 0xEC,
|
||||
0xEE, 0xF7, 0xF3, 0x70, 0x20, 0x00,
|
||||
0x7C, 0x7C, 0x7C, 0x7E, 0x00, 0x7E,
|
||||
0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x00,
|
||||
0x00, 0x80, 0xC0, 0xE0, 0x7E, 0x5B,
|
||||
0x4F, 0x5B, 0xFE, 0xC0, 0x00, 0x00,
|
||||
0xC0, 0x00, 0xDC, 0xD7, 0xDE, 0xDE,
|
||||
0xDE, 0xD7, 0xDC, 0x00, 0xC0, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
0xE0, 0x70, 0x78, 0x68, 0x6C, 0x64,
|
||||
0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7,
|
||||
0xE7, 0x3F, 0x1F, 0x0F, 0x3F, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F,
|
||||
0x0F, 0x00, 0x00, 0x00, 0xFF, 0xFF,
|
||||
0xFF, 0x1E, 0x3F, 0xFF, 0xF3, 0xE1,
|
||||
0x80, 0x00, 0x00, 0xF8, 0xFC, 0xFE,
|
||||
0x36, 0x36, 0x36, 0x3E, 0xBC, 0xB8,
|
||||
0x00, 0x0E, 0x3E, 0xFE, 0xF0, 0x80,
|
||||
0xF0, 0xFE, 0x3E, 0x0E, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0x06, 0x06, 0x06,
|
||||
0xFE, 0xFE, 0xFC, 0x70, 0x00, 0xE6,
|
||||
0xF6, 0xF6, 0x36, 0x36, 0xFE, 0xFE,
|
||||
0xFC, 0x00, 0x00, 0x00, 0xFF, 0xFF,
|
||||
0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0F, 0x1F, 0x3F, 0x7F, 0x7F, 0x7F,
|
||||
0x7F, 0x7F, 0x3F, 0x1E, 0x0C, 0x00,
|
||||
0x1F, 0x1F, 0x1F, 0x3F, 0x00, 0x3F,
|
||||
0x3F, 0x3F, 0x7F, 0x7F, 0x7F, 0x00,
|
||||
0x30, 0x7B, 0x7F, 0x78, 0x30, 0x20,
|
||||
0x20, 0x30, 0x78, 0x7F, 0x3B, 0x00,
|
||||
0x03, 0x00, 0x0F, 0x7F, 0x0F, 0x0F,
|
||||
0x0F, 0x7F, 0x0F, 0x00, 0x03, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x7F,
|
||||
0x63, 0x60, 0xE0, 0xC0, 0xC0, 0xC0,
|
||||
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF,
|
||||
0xFF, 0xC0, 0x60, 0x30, 0x18, 0x0F,
|
||||
0x03, 0x03, 0x03, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x03, 0x03,
|
||||
0x03, 0x00, 0x00, 0x00, 0x03, 0x03,
|
||||
0x03, 0x03, 0x00, 0x01, 0x03, 0x03,
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x01,
|
||||
0x00, 0x70, 0x70, 0x39, 0x3F, 0x1F,
|
||||
0x07, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x03, 0x03, 0x01, 0x00, 0x00, 0x01,
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x01, 0x00, 0x00, 0x00, 0x03, 0x03,
|
||||
0x03, 0x00, 0x00, 0x03, 0x03, 0x03,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
// clang-format on
|
128
keyboards/yowkees/lib/keyball/README.md
Normal file
128
keyboards/yowkees/lib/keyball/README.md
Normal file
@ -0,0 +1,128 @@
|
||||
# Keyball Core Function Library
|
||||
|
||||
## Scroll snap mode
|
||||
|
||||
When scrolling with the trackball, the scroll direction is restricted.
|
||||
This restriction is called "scroll snap".
|
||||
|
||||
The direction of restriction can be changed using special key codes.
|
||||
You can also release this restriction.
|
||||
It is called as "scroll snap mode"
|
||||
The current mode is displayed on the OLED.
|
||||
|
||||
There are 3 modes for scroll snap.
|
||||
|
||||
1. Vertical (default): key code is `SSNP_VRT`, indicated as `VT`.
|
||||
2. Horizontal: key code is `SSNP_HOR`, indicated as `HO`.
|
||||
3. Free: key code is `SSNP_FRE`, indicated as `SCR`.
|
||||
|
||||
The scroll snap mode at startup is vertical,
|
||||
but you can change it by saving the current mode with `KBC_SAVE`
|
||||
|
||||
## MEMO
|
||||
|
||||
This section contains notes regarding the specifications of this library.
|
||||
Since the purpose is to keep a record in whatever form it takes,
|
||||
a lot of Japanese is included.
|
||||
If you would like to read it in English, please request a translation via issue or discussion.
|
||||
Of course you can translate it for us. If you translate it,
|
||||
please make pull requests to share it us.
|
||||
|
||||
### Scroll Snap Spec
|
||||
|
||||
この機能は config.h に `#define KEYBALL_SCROLLSNAP_ENABLE 0` を書き加えることで無効化できる。
|
||||
|
||||
トラックボールによるスクロールの方向を制限するのがスクロールスナップ。
|
||||
現在のスクロールスナップには3つのモードがある。
|
||||
|
||||
* 垂直方向にスナップ (デフォルト)
|
||||
* 水平方向にスナップ
|
||||
* スナップしない自由スクロール
|
||||
|
||||
以上を `SSNP_VRT`, `SSNP_HOR`, `SSNP_FRE` の独自キーコードを用いて手動で切り替える。
|
||||
|
||||
#### up to 1.3.2
|
||||
|
||||
初期状態でトラックボールによるスクロールを垂直方向に制限(スナップ)している。
|
||||
この振る舞いは config.h に `#define KEYBALL_SCROLLSNAP_ENABLE 1` を書き加えることで有効化できる。
|
||||
|
||||
この機能はスナップモードとフリーモードから構成される。
|
||||
初期状態はスナップモードで、このモードではスクロール方向は垂直に制限される。
|
||||
スナップモードで水平の一定方向に一定カウント(デフォルトは12)以上スクロールするとフリーモードに遷移する。
|
||||
なおこのカウントはスクロール除数適用後のカウントである。
|
||||
フリーモードでは制限が取り払われ、水平と垂直どちらにも自由にスクロールできる。
|
||||
フリーモードで一定時間(デフォルトは 100 ミリ秒)、スクロール操作を行わないとスナップモードに遷移する。
|
||||
|
||||
フリーモードに遷移するためのカウント数を変更するには `KEYBALL_SCROLLSNAP_TENSION_THRESHOLD` を、
|
||||
スナップモードに遷移するためのインターバル(ミリ秒)を変更するには `KEYBALL_SCROLLSNAP_RESET_TIMER` を、それぞれ config.h で設定できる。
|
||||
|
||||
以下はカウント数を 5 に、インターバルを 200 ミリ秒に変更する例:
|
||||
|
||||
```c
|
||||
#define KEYBALL_SCROLLSNAP_TENSION_THRESHOLD 5
|
||||
#define KEYBALL_SCROLLSNAP_RESET_TIMER 200
|
||||
```
|
||||
|
||||
#### History of Scroll Snap
|
||||
|
||||
もともとは自由にスクロールできるようにしていた。
|
||||
しかし思ったよりもボールの感度が高く一定方向だけに動かすのが難しく、
|
||||
誤操作を誘発していた。
|
||||
そのためなんらかのスナップ機能が必要だと判断した。
|
||||
|
||||
最初のスナップ機能は垂直・水平のどちらかの軸から一定角度以内で収まってるうちはそちらへスナップするとした。
|
||||
しかし回転開始初期にはその移動量が極小かつセンサーの感度が高いので、
|
||||
垂直に動かしたい時に水平にも極小量の移動が発生しておりスナップ方向が定まらない、
|
||||
という問題が発生した。
|
||||
人間は自分が思うほどには指を正確に動かせていなかった。
|
||||
|
||||
そこで一定方向に一定以上のカウントを検出するまでは一切スクロールしないようにした。
|
||||
これは回転開始初期のスクロール量を読み捨てるに等しい。
|
||||
|
||||
しかしWebブラウザを思い浮かべてもらえればすぐにわかるように、
|
||||
一般のユースケースでは垂直方向のスクロールを頻繁に利用する。
|
||||
先の読み捨てにより、垂直方向のスクロールがワンテンポ遅れ、体験を大幅に損なうことが明らかになった。
|
||||
この解決のためモード: 初期は垂直のみ、後に自由スクロールする、を導入した。
|
||||
|
||||
### Scroll Divider / スクロール除数
|
||||
|
||||
Keyballのセンサーは感度がとても高い。
|
||||
そのため生の値をスクロール量としてしまうとスクロール操作がとても難しくなった。
|
||||
そこで生の値をある数で割ってスクロールに適用する方式を採用した。
|
||||
この時の割る数をスクロール除数と言っている。
|
||||
|
||||
スクロール除数は、体感として小さく制御する意味がなかったので、
|
||||
1, 2, 4, 8, 16, 32, 64 というように2の乗数とした。
|
||||
2の乗数であるのならば値の表現として $2 ^ n$ の $n$ で表せる。
|
||||
またEEPROMに設定値を保存できるようにするために、
|
||||
ビット数を節約する目的で $n$ が取りうる値は 1~7 の範囲とした。
|
||||
結果実際の割る数は以下の式で計算できる。
|
||||
|
||||
$$ 2 ^ {(n - 1)} $$
|
||||
|
||||
$n$ の初期値は 4 で 1/8 になることを意味する。
|
||||
この値は config.h で `KEYBALL_SCROLL_DIV_DEFAULT` マクロを定義することで変更できる。
|
||||
これを0にすることは考慮していないので設定しないこと。
|
||||
|
||||
### Scroll Inhivitor
|
||||
|
||||
トラックボールの移動量をポインタに適用するかスクロールに適用するか、
|
||||
Keyballは内部にスクロールモードという名のモードで管理している。
|
||||
スクロールモードはキーコードやAPI呼び出しの任意のタイミングで切り替えられる。
|
||||
デフォルトのキーマップでは特定のレイヤーの状態を
|
||||
スクロールモードのオンオフに適用している。
|
||||
|
||||
当初はスクロールモードの切り替え直後に、
|
||||
トラックボールの移動が意図しない適用先に適用されることが頻発した。
|
||||
ポインタをブラウザまで動かした後にスクロールモードに変更すると、
|
||||
意図していない方向にスクロールするといった体験になる。
|
||||
|
||||
そこでスクロールモード切替直後の一定時間は
|
||||
一切のトラックボール操作を読み捨てることにした。
|
||||
この読み捨てる時間のことを Scroll Inhivitor と名付けた。
|
||||
この Scroll Inhivitor のデフォルト値は 50 ミリ秒である。
|
||||
短い時間ではあるが結構効いている。
|
||||
|
||||
Scroll Inhivitor は config.h で `KEYBALL_SCROLLBALL_INHIVITOR` マクロを定義することで変更できる。
|
||||
無効化したい場合は値として `0` を設定する。
|
||||
興味があれば無効にしてみるのも面白いかもしれない。
|
736
keyboards/yowkees/lib/keyball/keyball.c
Normal file
736
keyboards/yowkees/lib/keyball/keyball.c
Normal file
@ -0,0 +1,736 @@
|
||||
/*
|
||||
Copyright 2022 MURAOKA Taro (aka KoRoN, @kaoriya)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "quantum.h"
|
||||
#ifdef SPLIT_KEYBOARD
|
||||
# include "transactions.h"
|
||||
#endif
|
||||
|
||||
#include "keyball.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
const uint8_t CPI_DEFAULT = KEYBALL_CPI_DEFAULT / 100;
|
||||
/**
|
||||
* The Keyball library uses a range of 0 <= cpi <= 119 internally, and the true
|
||||
* CPI value is ( cpi + 1 ) * 100.
|
||||
*/
|
||||
const uint8_t CPI_MAX = KEYBALL_PMW3360_MAXCPI + 1; // 119 + 1 = 12000 CPI actual
|
||||
const uint8_t SCROLL_DIV_MAX = 7;
|
||||
|
||||
const uint16_t AML_TIMEOUT_MIN = 100;
|
||||
const uint16_t AML_TIMEOUT_MAX = 1000;
|
||||
const uint16_t AML_TIMEOUT_QU = 50; // Quantization Unit
|
||||
|
||||
static const char BL = '\xB0'; // Blank indicator character
|
||||
static const char LFSTR_ON[] PROGMEM = "\xB2\xB3";
|
||||
static const char LFSTR_OFF[] PROGMEM = "\xB4\xB5";
|
||||
|
||||
keyball_t keyball = {
|
||||
.this_have_ball = false,
|
||||
.that_enable = false,
|
||||
.that_have_ball = false,
|
||||
|
||||
.this_motion = {0},
|
||||
.that_motion = {0},
|
||||
|
||||
.cpi_value = 0,
|
||||
.cpi_changed = false,
|
||||
|
||||
.scroll_mode = false,
|
||||
.scroll_div = 0,
|
||||
|
||||
.pressing_keys = { '\xB0', '\xB0', '\xB0', '\xB0', '\xB0', '\xB0', 0 },
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Hook points
|
||||
|
||||
__attribute__((weak)) void keyball_on_adjust_layout(keyball_adjust_t v) {}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Static utilities
|
||||
|
||||
// add16 adds two int16_t with clipping.
|
||||
static int16_t add16(int16_t a, int16_t b) {
|
||||
int16_t r = a + b;
|
||||
if (a >= 0 && b >= 0 && r < 0) {
|
||||
r = 32767;
|
||||
} else if (a < 0 && b < 0 && r >= 0) {
|
||||
r = -32768;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// divmod16 divides *v by div, returns the quotient, and assigns the remainder
|
||||
// to *v.
|
||||
static int16_t divmod16(int16_t *v, int16_t div) {
|
||||
int16_t r = *v / div;
|
||||
*v -= r * div;
|
||||
return r;
|
||||
}
|
||||
|
||||
// clip2int8 clips an integer fit into int8_t.
|
||||
static inline int8_t clip2int8(int16_t v) {
|
||||
return (v) < -127 ? -127 : (v) > 127 ? 127 : (int8_t)v;
|
||||
}
|
||||
|
||||
#ifdef OLED_ENABLE
|
||||
static const char *format_4d(int8_t d) {
|
||||
static char buf[5] = {0}; // max width (4) + NUL (1)
|
||||
char lead = ' ';
|
||||
if (d < 0) {
|
||||
d = -d;
|
||||
lead = '-';
|
||||
}
|
||||
buf[3] = (d % 10) + '0';
|
||||
d /= 10;
|
||||
if (d == 0) {
|
||||
buf[2] = lead;
|
||||
lead = ' ';
|
||||
} else {
|
||||
buf[2] = (d % 10) + '0';
|
||||
d /= 10;
|
||||
}
|
||||
if (d == 0) {
|
||||
buf[1] = lead;
|
||||
lead = ' ';
|
||||
} else {
|
||||
buf[1] = (d % 10) + '0';
|
||||
d /= 10;
|
||||
}
|
||||
buf[0] = lead;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char to_1x(uint8_t x) {
|
||||
x &= 0x0f;
|
||||
return x < 10 ? x + '0' : x + 'a' - 10;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void add_cpi(int8_t delta) {
|
||||
int16_t v = keyball_get_cpi() + delta;
|
||||
keyball_set_cpi(v < 1 ? 1 : v);
|
||||
}
|
||||
|
||||
static void add_scroll_div(int8_t delta) {
|
||||
int8_t v = keyball_get_scroll_div() + delta;
|
||||
keyball_set_scroll_div(v < 1 ? 1 : v);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Pointing device driver
|
||||
|
||||
#if KEYBALL_MODEL == 46
|
||||
void keyboard_pre_init_kb(void) {
|
||||
keyball.this_have_ball = pmw3360_init();
|
||||
keyboard_pre_init_user();
|
||||
}
|
||||
#endif
|
||||
|
||||
__attribute__((weak)) void keyball_on_apply_motion_to_mouse_move(keyball_motion_t *m, report_mouse_t *r, bool is_left) {
|
||||
#if KEYBALL_MODEL == 61 || KEYBALL_MODEL == 39 || KEYBALL_MODEL == 147 || KEYBALL_MODEL == 44
|
||||
r->x = clip2int8(m->y);
|
||||
r->y = clip2int8(m->x);
|
||||
if (is_left) {
|
||||
r->x = -r->x;
|
||||
r->y = -r->y;
|
||||
}
|
||||
#elif KEYBALL_MODEL == 46
|
||||
r->x = clip2int8(m->x);
|
||||
r->y = -clip2int8(m->y);
|
||||
#else
|
||||
# error("unknown Keyball model")
|
||||
#endif
|
||||
// clear motion
|
||||
m->x = 0;
|
||||
m->y = 0;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void keyball_on_apply_motion_to_mouse_scroll(keyball_motion_t *m, report_mouse_t *r, bool is_left) {
|
||||
// consume motion of trackball.
|
||||
int16_t div = 1 << (keyball_get_scroll_div() - 1);
|
||||
int16_t x = divmod16(&m->x, div);
|
||||
int16_t y = divmod16(&m->y, div);
|
||||
|
||||
// apply to mouse report.
|
||||
#if KEYBALL_MODEL == 61 || KEYBALL_MODEL == 39 || KEYBALL_MODEL == 147 || KEYBALL_MODEL == 44
|
||||
r->h = clip2int8(y);
|
||||
r->v = -clip2int8(x);
|
||||
if (is_left) {
|
||||
r->h = -r->h;
|
||||
r->v = -r->v;
|
||||
}
|
||||
#elif KEYBALL_MODEL == 46
|
||||
r->h = clip2int8(x);
|
||||
r->v = clip2int8(y);
|
||||
#else
|
||||
# error("unknown Keyball model")
|
||||
#endif
|
||||
|
||||
// Scroll snapping
|
||||
#if KEYBALL_SCROLLSNAP_ENABLE == 1
|
||||
// Old behavior up to 1.3.2)
|
||||
uint32_t now = timer_read32();
|
||||
if (r->h != 0 || r->v != 0) {
|
||||
keyball.scroll_snap_last = now;
|
||||
} else if (TIMER_DIFF_32(now, keyball.scroll_snap_last) >= KEYBALL_SCROLLSNAP_RESET_TIMER) {
|
||||
keyball.scroll_snap_tension_h = 0;
|
||||
}
|
||||
if (abs(keyball.scroll_snap_tension_h) < KEYBALL_SCROLLSNAP_TENSION_THRESHOLD) {
|
||||
keyball.scroll_snap_tension_h += y;
|
||||
r->h = 0;
|
||||
}
|
||||
#elif KEYBALL_SCROLLSNAP_ENABLE == 2
|
||||
// New behavior
|
||||
switch (keyball_get_scrollsnap_mode()) {
|
||||
case KEYBALL_SCROLLSNAP_MODE_VERTICAL:
|
||||
r->h = 0;
|
||||
break;
|
||||
case KEYBALL_SCROLLSNAP_MODE_HORIZONTAL:
|
||||
r->v = 0;
|
||||
break;
|
||||
default:
|
||||
// pass by without doing anything
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool should_report(void) {
|
||||
uint32_t now = timer_read32();
|
||||
#if defined(KEYBALL_REPORTMOUSE_INTERVAL) && KEYBALL_REPORTMOUSE_INTERVAL > 0
|
||||
// throttling mouse report rate.
|
||||
static uint32_t last = 0;
|
||||
if (TIMER_DIFF_32(now, last) < KEYBALL_REPORTMOUSE_INTERVAL) {
|
||||
return false;
|
||||
}
|
||||
last = now;
|
||||
#endif
|
||||
#if defined(KEYBALL_SCROLLBALL_INHIVITOR) && KEYBALL_SCROLLBALL_INHIVITOR > 0
|
||||
if (TIMER_DIFF_32(now, keyball.scroll_mode_changed) < KEYBALL_SCROLLBALL_INHIVITOR) {
|
||||
keyball.this_motion.x = 0;
|
||||
keyball.this_motion.y = 0;
|
||||
keyball.that_motion.x = 0;
|
||||
keyball.that_motion.y = 0;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Split RPC
|
||||
|
||||
#ifdef SPLIT_KEYBOARD
|
||||
|
||||
static void rpc_get_info_handler(uint8_t in_buflen, const void *in_data, uint8_t out_buflen, void *out_data) {
|
||||
keyball_info_t info = {
|
||||
.ballcnt = keyball.this_have_ball ? 1 : 0,
|
||||
};
|
||||
*(keyball_info_t *)out_data = info;
|
||||
keyball_on_adjust_layout(KEYBALL_ADJUST_SECONDARY);
|
||||
}
|
||||
|
||||
static void rpc_get_info_invoke(void) {
|
||||
static bool negotiated = false;
|
||||
static uint32_t last_sync = 0;
|
||||
static int round = 0;
|
||||
uint32_t now = timer_read32();
|
||||
if (negotiated || TIMER_DIFF_32(now, last_sync) < KEYBALL_TX_GETINFO_INTERVAL) {
|
||||
return;
|
||||
}
|
||||
last_sync = now;
|
||||
round++;
|
||||
keyball_info_t recv = {0};
|
||||
if (!transaction_rpc_exec(KEYBALL_GET_INFO, 0, NULL, sizeof(recv), &recv)) {
|
||||
if (round < KEYBALL_TX_GETINFO_MAXTRY) {
|
||||
dprintf("keyball:rpc_get_info_invoke: missed #%d\n", round);
|
||||
return;
|
||||
}
|
||||
}
|
||||
negotiated = true;
|
||||
keyball.that_enable = true;
|
||||
keyball.that_have_ball = recv.ballcnt > 0;
|
||||
dprintf("keyball:rpc_get_info_invoke: negotiated #%d %d\n", round, keyball.that_have_ball);
|
||||
|
||||
// split keyboard negotiation completed.
|
||||
|
||||
# ifdef VIA_ENABLE
|
||||
// adjust VIA layout options according to current combination.
|
||||
uint8_t layouts = (keyball.this_have_ball ? (is_keyboard_left() ? 0x02 : 0x01) : 0x00) | (keyball.that_have_ball ? (is_keyboard_left() ? 0x01 : 0x02) : 0x00);
|
||||
uint32_t curr = via_get_layout_options();
|
||||
uint32_t next = (curr & ~0x3) | layouts;
|
||||
if (next != curr) {
|
||||
via_set_layout_options(next);
|
||||
}
|
||||
# endif
|
||||
|
||||
keyball_on_adjust_layout(KEYBALL_ADJUST_PRIMARY);
|
||||
}
|
||||
|
||||
static void rpc_get_motion_handler(uint8_t in_buflen, const void *in_data, uint8_t out_buflen, void *out_data) {
|
||||
*(keyball_motion_t *)out_data = keyball.this_motion;
|
||||
// clear motion
|
||||
keyball.this_motion.x = 0;
|
||||
keyball.this_motion.y = 0;
|
||||
}
|
||||
|
||||
static void rpc_get_motion_invoke(void) {
|
||||
static uint32_t last_sync = 0;
|
||||
uint32_t now = timer_read32();
|
||||
if (TIMER_DIFF_32(now, last_sync) < KEYBALL_TX_GETMOTION_INTERVAL) {
|
||||
return;
|
||||
}
|
||||
keyball_motion_t recv = {0};
|
||||
if (transaction_rpc_exec(KEYBALL_GET_MOTION, 0, NULL, sizeof(recv), &recv)) {
|
||||
keyball.that_motion.x = add16(keyball.that_motion.x, recv.x);
|
||||
keyball.that_motion.y = add16(keyball.that_motion.y, recv.y);
|
||||
}
|
||||
last_sync = now;
|
||||
return;
|
||||
}
|
||||
|
||||
static void rpc_set_cpi_handler(uint8_t in_buflen, const void *in_data, uint8_t out_buflen, void *out_data) {
|
||||
keyball_set_cpi(*(keyball_cpi_t *)in_data);
|
||||
}
|
||||
|
||||
static void rpc_set_cpi_invoke(void) {
|
||||
if (!keyball.cpi_changed) {
|
||||
return;
|
||||
}
|
||||
keyball_cpi_t req = keyball.cpi_value;
|
||||
if (!transaction_rpc_send(KEYBALL_SET_CPI, sizeof(req), &req)) {
|
||||
return;
|
||||
}
|
||||
keyball.cpi_changed = false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// OLED utility
|
||||
|
||||
#ifdef OLED_ENABLE
|
||||
// clang-format off
|
||||
const char PROGMEM code_to_name[] = {
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
|
||||
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
|
||||
'u', 'v', 'w', 'x', 'y', 'z', '1', '2', '3', '4',
|
||||
'5', '6', '7', '8', '9', '0', 'R', 'E', 'B', 'T',
|
||||
'_', '-', '=', '[', ']', '\\', '#', ';', '\'', '`',
|
||||
',', '.', '/',
|
||||
};
|
||||
// clang-format on
|
||||
#endif
|
||||
|
||||
void keyball_oled_render_ballinfo(void) {
|
||||
#ifdef OLED_ENABLE
|
||||
// Format: `Ball:{mouse x}{mouse y}{mouse h}{mouse v}`
|
||||
//
|
||||
// Output example:
|
||||
//
|
||||
// Ball: -12 34 0 0
|
||||
|
||||
// 1st line, "Ball" label, mouse x, y, h, and v.
|
||||
oled_write_P(PSTR("Ball\xB1"), false);
|
||||
oled_write(format_4d(keyball.last_mouse.x), false);
|
||||
oled_write(format_4d(keyball.last_mouse.y), false);
|
||||
oled_write(format_4d(keyball.last_mouse.h), false);
|
||||
oled_write(format_4d(keyball.last_mouse.v), false);
|
||||
|
||||
// 2nd line, empty label and CPI
|
||||
oled_write_P(PSTR(" \xB1\xBC\xBD"), false);
|
||||
oled_write(format_4d(keyball_get_cpi()) + 1, false);
|
||||
oled_write_P(PSTR("00 "), false);
|
||||
|
||||
// indicate scroll snap mode: "VT" (vertical), "HN" (horiozntal), and "SCR" (free)
|
||||
#if 1 && KEYBALL_SCROLLSNAP_ENABLE == 2
|
||||
switch (keyball_get_scrollsnap_mode()) {
|
||||
case KEYBALL_SCROLLSNAP_MODE_VERTICAL:
|
||||
oled_write_P(PSTR("VT"), false);
|
||||
break;
|
||||
case KEYBALL_SCROLLSNAP_MODE_HORIZONTAL:
|
||||
oled_write_P(PSTR("HO"), false);
|
||||
break;
|
||||
default:
|
||||
oled_write_P(PSTR("\xBE\xBF"), false);
|
||||
break;
|
||||
}
|
||||
#else
|
||||
oled_write_P(PSTR("\xBE\xBF"), false);
|
||||
#endif
|
||||
// indicate scroll mode: on/off
|
||||
if (keyball.scroll_mode) {
|
||||
oled_write_P(LFSTR_ON, false);
|
||||
} else {
|
||||
oled_write_P(LFSTR_OFF, false);
|
||||
}
|
||||
|
||||
// indicate scroll divider:
|
||||
oled_write_P(PSTR(" \xC0\xC1"), false);
|
||||
oled_write_char('0' + keyball_get_scroll_div(), false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void keyball_oled_render_ballsubinfo(void) {
|
||||
#ifdef OLED_ENABLE
|
||||
#endif
|
||||
}
|
||||
|
||||
void keyball_oled_render_keyinfo(void) {
|
||||
#ifdef OLED_ENABLE
|
||||
// Format: `Key : R{row} C{col} K{kc} {name}{name}{name}`
|
||||
//
|
||||
// Where `kc` is lower 8 bit of keycode.
|
||||
// Where `name`s are readable labels for pressing keys, valid between 4 and 56.
|
||||
//
|
||||
// `row`, `col`, and `kc` indicates the last processed key,
|
||||
// but `name`s indicate unreleased keys in best effort.
|
||||
//
|
||||
// It is aligned to fit with output of keyball_oled_render_ballinfo().
|
||||
// For example:
|
||||
//
|
||||
// Key : R2 C3 K06 abc
|
||||
// Ball: 0 0 0 0
|
||||
|
||||
// "Key" Label
|
||||
oled_write_P(PSTR("Key \xB1"), false);
|
||||
|
||||
// Row and column
|
||||
oled_write_char('\xB8', false);
|
||||
oled_write_char(to_1x(keyball.last_pos.row), false);
|
||||
oled_write_char('\xB9', false);
|
||||
oled_write_char(to_1x(keyball.last_pos.col), false);
|
||||
|
||||
// Keycode
|
||||
oled_write_P(PSTR("\xBA\xBB"), false);
|
||||
oled_write_char(to_1x(keyball.last_kc >> 4), false);
|
||||
oled_write_char(to_1x(keyball.last_kc), false);
|
||||
|
||||
// Pressing keys
|
||||
oled_write_P(PSTR(" "), false);
|
||||
oled_write(keyball.pressing_keys, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void keyball_oled_render_layerinfo(void) {
|
||||
#ifdef OLED_ENABLE
|
||||
// Format: `Layer:{layer state}`
|
||||
//
|
||||
// Output example:
|
||||
//
|
||||
// Layer:-23------------
|
||||
//
|
||||
oled_write_P(PSTR("L\xB6\xB7r\xB1"), false);
|
||||
for (uint8_t i = 1; i < 8; i++) {
|
||||
oled_write_char((layer_state_is(i) ? to_1x(i) : BL), false);
|
||||
}
|
||||
oled_write_char(' ', false);
|
||||
|
||||
# ifdef POINTING_DEVICE_AUTO_MOUSE_ENABLE
|
||||
oled_write_P(PSTR("\xC2\xC3"), false);
|
||||
if (get_auto_mouse_enable()) {
|
||||
oled_write_P(LFSTR_ON, false);
|
||||
} else {
|
||||
oled_write_P(LFSTR_OFF, false);
|
||||
}
|
||||
|
||||
oled_write(format_4d(get_auto_mouse_timeout() / 10) + 1, false);
|
||||
oled_write_char('0', false);
|
||||
# else
|
||||
oled_write_P(PSTR("\xC2\xC3\xB4\xB5 ---"), false);
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Public API functions
|
||||
|
||||
bool keyball_get_scroll_mode(void) {
|
||||
return keyball.scroll_mode;
|
||||
}
|
||||
|
||||
void keyball_set_scroll_mode(bool mode) {
|
||||
if (mode != keyball.scroll_mode) {
|
||||
keyball.scroll_mode_changed = timer_read32();
|
||||
}
|
||||
keyball.scroll_mode = mode;
|
||||
}
|
||||
|
||||
keyball_scrollsnap_mode_t keyball_get_scrollsnap_mode(void) {
|
||||
#if KEYBALL_SCROLLSNAP_ENABLE == 2
|
||||
return keyball.scrollsnap_mode;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void keyball_set_scrollsnap_mode(keyball_scrollsnap_mode_t mode) {
|
||||
#if KEYBALL_SCROLLSNAP_ENABLE == 2
|
||||
keyball.scrollsnap_mode = mode;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint8_t keyball_get_scroll_div(void) {
|
||||
return keyball.scroll_div == 0 ? KEYBALL_SCROLL_DIV_DEFAULT : keyball.scroll_div;
|
||||
}
|
||||
|
||||
void keyball_set_scroll_div(uint8_t div) {
|
||||
keyball.scroll_div = div > SCROLL_DIV_MAX ? SCROLL_DIV_MAX : div;
|
||||
}
|
||||
|
||||
uint8_t keyball_get_cpi(void) {
|
||||
return keyball.cpi_value == 0 ? CPI_DEFAULT : keyball.cpi_value;
|
||||
}
|
||||
|
||||
void keyball_set_cpi(uint8_t cpi) {
|
||||
if (cpi > CPI_MAX) {
|
||||
cpi = CPI_MAX;
|
||||
}
|
||||
keyball.cpi_value = cpi;
|
||||
keyball.cpi_changed = true;
|
||||
if (keyball.this_have_ball) {
|
||||
/**
|
||||
* QMK's core PMW3360 driver uses the true CPI value internally, so we
|
||||
* have to translate the Keyball library's value to QMK's expected
|
||||
* range.
|
||||
*
|
||||
* QMK's core driver also caps the range internally to a valid CPI
|
||||
* value, so we don't need to do it here.
|
||||
*/
|
||||
pmw33xx_set_cpi(0, (cpi + 1) * 100);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Keyboard hooks
|
||||
|
||||
void keyboard_post_init_kb(void) {
|
||||
#ifdef SPLIT_KEYBOARD
|
||||
// register transaction handlers on secondary.
|
||||
if (!is_keyboard_master()) {
|
||||
transaction_register_rpc(KEYBALL_GET_INFO, rpc_get_info_handler);
|
||||
transaction_register_rpc(KEYBALL_GET_MOTION, rpc_get_motion_handler);
|
||||
transaction_register_rpc(KEYBALL_SET_CPI, rpc_set_cpi_handler);
|
||||
}
|
||||
#endif
|
||||
|
||||
// read keyball configuration from EEPROM
|
||||
if (eeconfig_is_enabled()) {
|
||||
keyball_config_t c = {.raw = eeconfig_read_kb()};
|
||||
keyball_set_cpi(c.cpi);
|
||||
keyball_set_scroll_div(c.sdiv);
|
||||
#ifdef POINTING_DEVICE_AUTO_MOUSE_ENABLE
|
||||
set_auto_mouse_enable(c.amle);
|
||||
set_auto_mouse_timeout(c.amlto == 0 ? AUTO_MOUSE_TIME : (c.amlto + 1) * AML_TIMEOUT_QU);
|
||||
#endif
|
||||
#if KEYBALL_SCROLLSNAP_ENABLE == 2
|
||||
keyball_set_scrollsnap_mode(c.ssnap);
|
||||
#endif
|
||||
}
|
||||
|
||||
keyball_on_adjust_layout(KEYBALL_ADJUST_PENDING);
|
||||
keyboard_post_init_user();
|
||||
}
|
||||
|
||||
#if SPLIT_KEYBOARD
|
||||
void housekeeping_task_kb(void) {
|
||||
if (is_keyboard_master()) {
|
||||
rpc_get_info_invoke();
|
||||
if (keyball.that_have_ball) {
|
||||
rpc_get_motion_invoke();
|
||||
rpc_set_cpi_invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void pressing_keys_update(uint16_t keycode, keyrecord_t *record) {
|
||||
// Process only valid keycodes.
|
||||
if (keycode >= 4 && keycode < 57) {
|
||||
char value = pgm_read_byte(code_to_name + keycode - 4);
|
||||
char where = BL;
|
||||
if (!record->event.pressed) {
|
||||
// Swap `value` and `where` when releasing.
|
||||
where = value;
|
||||
value = BL;
|
||||
}
|
||||
// Rewrite the last `where` of pressing_keys to `value` .
|
||||
for (int i = 0; i < KEYBALL_OLED_MAX_PRESSING_KEYCODES; i++) {
|
||||
if (keyball.pressing_keys[i] == where) {
|
||||
keyball.pressing_keys[i] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef POINTING_DEVICE_AUTO_MOUSE_ENABLE
|
||||
bool is_mouse_record_kb(uint16_t keycode, keyrecord_t* record) {
|
||||
switch (keycode) {
|
||||
case SCRL_MO:
|
||||
return true;
|
||||
}
|
||||
return is_mouse_record_user(keycode, record);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
|
||||
// store last keycode, row, and col for OLED
|
||||
keyball.last_kc = keycode;
|
||||
keyball.last_pos = record->event.key;
|
||||
|
||||
pressing_keys_update(keycode, record);
|
||||
|
||||
if (!process_record_user(keycode, record)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// strip QK_MODS part.
|
||||
if (keycode >= QK_MODS && keycode <= QK_MODS_MAX) {
|
||||
keycode &= 0xff;
|
||||
}
|
||||
|
||||
switch (keycode) {
|
||||
#ifndef MOUSEKEY_ENABLE
|
||||
// process KC_MS_BTN1~8 by myself
|
||||
// See process_action() in quantum/action.c for details.
|
||||
case KC_MS_BTN1 ... KC_MS_BTN8: {
|
||||
extern void register_mouse(uint8_t mouse_keycode, bool pressed);
|
||||
register_mouse(keycode, record->event.pressed);
|
||||
// to apply QK_MODS actions, allow to process others.
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
case SCRL_MO:
|
||||
keyball_set_scroll_mode(record->event.pressed);
|
||||
// process_auto_mouse may use this in future, if changed order of
|
||||
// processes.
|
||||
return true;
|
||||
}
|
||||
|
||||
// process events which works on pressed only.
|
||||
if (record->event.pressed) {
|
||||
switch (keycode) {
|
||||
case KBC_RST:
|
||||
keyball_set_cpi(0);
|
||||
keyball_set_scroll_div(0);
|
||||
#ifdef POINTING_DEVICE_AUTO_MOUSE_ENABLE
|
||||
set_auto_mouse_enable(false);
|
||||
set_auto_mouse_timeout(AUTO_MOUSE_TIME);
|
||||
#endif
|
||||
break;
|
||||
case KBC_SAVE: {
|
||||
keyball_config_t c = {
|
||||
.cpi = keyball.cpi_value,
|
||||
.sdiv = keyball.scroll_div,
|
||||
#ifdef POINTING_DEVICE_AUTO_MOUSE_ENABLE
|
||||
.amle = get_auto_mouse_enable(),
|
||||
.amlto = (get_auto_mouse_timeout() / AML_TIMEOUT_QU) - 1,
|
||||
#endif
|
||||
#if KEYBALL_SCROLLSNAP_ENABLE == 2
|
||||
.ssnap = keyball_get_scrollsnap_mode(),
|
||||
#endif
|
||||
};
|
||||
eeconfig_update_kb(c.raw);
|
||||
} break;
|
||||
|
||||
case CPI_I100:
|
||||
add_cpi(1);
|
||||
break;
|
||||
case CPI_D100:
|
||||
add_cpi(-1);
|
||||
break;
|
||||
case CPI_I1K:
|
||||
add_cpi(10);
|
||||
break;
|
||||
case CPI_D1K:
|
||||
add_cpi(-10);
|
||||
break;
|
||||
|
||||
case SCRL_TO:
|
||||
keyball_set_scroll_mode(!keyball.scroll_mode);
|
||||
break;
|
||||
case SCRL_DVI:
|
||||
add_scroll_div(1);
|
||||
break;
|
||||
case SCRL_DVD:
|
||||
add_scroll_div(-1);
|
||||
break;
|
||||
|
||||
#if KEYBALL_SCROLLSNAP_ENABLE == 2
|
||||
case SSNP_HOR:
|
||||
keyball_set_scrollsnap_mode(KEYBALL_SCROLLSNAP_MODE_HORIZONTAL);
|
||||
break;
|
||||
case SSNP_VRT:
|
||||
keyball_set_scrollsnap_mode(KEYBALL_SCROLLSNAP_MODE_VERTICAL);
|
||||
break;
|
||||
case SSNP_FRE:
|
||||
keyball_set_scrollsnap_mode(KEYBALL_SCROLLSNAP_MODE_FREE);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef POINTING_DEVICE_AUTO_MOUSE_ENABLE
|
||||
case AML_TO:
|
||||
set_auto_mouse_enable(!get_auto_mouse_enable());
|
||||
break;
|
||||
case AML_I50:
|
||||
{
|
||||
uint16_t v = get_auto_mouse_timeout() + 50;
|
||||
set_auto_mouse_timeout(MIN(v, AML_TIMEOUT_MAX));
|
||||
}
|
||||
break;
|
||||
case AML_D50:
|
||||
{
|
||||
uint16_t v = get_auto_mouse_timeout() - 50;
|
||||
set_auto_mouse_timeout(MAX(v, AML_TIMEOUT_MIN));
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Disable functions keycode_config() and mod_config() in keycode_config.c to
|
||||
// reduce size. These functions are provided for customizing magic keycode.
|
||||
// These two functions are mostly unnecessary if `MAGIC_KEYCODE_ENABLE = no` is
|
||||
// set.
|
||||
//
|
||||
// If `MAGIC_KEYCODE_ENABLE = no` and you want to keep these two functions as
|
||||
// they are, define the macro KEYBALL_KEEP_MAGIC_FUNCTIONS.
|
||||
//
|
||||
// See: https://docs.qmk.fm/#/squeezing_avr?id=magic-functions
|
||||
//
|
||||
#if !defined(MAGIC_KEYCODE_ENABLE) && !defined(KEYBALL_KEEP_MAGIC_FUNCTIONS)
|
||||
|
||||
uint16_t keycode_config(uint16_t keycode) {
|
||||
return keycode;
|
||||
}
|
||||
|
||||
uint8_t mod_config(uint8_t mod) {
|
||||
return mod;
|
||||
}
|
||||
|
||||
#endif
|
276
keyboards/yowkees/lib/keyball/keyball.h
Normal file
276
keyboards/yowkees/lib/keyball/keyball.h
Normal file
@ -0,0 +1,276 @@
|
||||
/*
|
||||
Copyright 2022 MURAOKA Taro (aka KoRoN, @kaoriya)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Configurations
|
||||
|
||||
#ifndef KEYBALL_CPI_DEFAULT
|
||||
# define KEYBALL_CPI_DEFAULT 500
|
||||
#endif
|
||||
|
||||
#ifndef KEYBALL_SCROLL_DIV_DEFAULT
|
||||
# define KEYBALL_SCROLL_DIV_DEFAULT 4 // 4: 1/8 (1/2^(n-1))
|
||||
#endif
|
||||
|
||||
#ifndef KEYBALL_REPORTMOUSE_INTERVAL
|
||||
# define KEYBALL_REPORTMOUSE_INTERVAL 8 // mouse report rate: 125Hz
|
||||
#endif
|
||||
|
||||
#ifndef KEYBALL_SCROLLBALL_INHIVITOR
|
||||
# define KEYBALL_SCROLLBALL_INHIVITOR 50
|
||||
#endif
|
||||
|
||||
/// To disable scroll snap feature, define 0 in your config.h
|
||||
#ifndef KEYBALL_SCROLLSNAP_ENABLE
|
||||
# define KEYBALL_SCROLLSNAP_ENABLE 2
|
||||
#endif
|
||||
|
||||
#ifndef KEYBALL_SCROLLSNAP_RESET_TIMER
|
||||
# define KEYBALL_SCROLLSNAP_RESET_TIMER 100
|
||||
#endif
|
||||
|
||||
#ifndef KEYBALL_SCROLLSNAP_TENSION_THRESHOLD
|
||||
# define KEYBALL_SCROLLSNAP_TENSION_THRESHOLD 12
|
||||
#endif
|
||||
|
||||
#ifndef KEYBALL_PMW3360_MAXCPI
|
||||
# define KEYBALL_PMW3360_MAXCPI 119 // 12000 CPI actual
|
||||
#endif
|
||||
|
||||
/// Specify SROM ID to be uploaded PMW3360DW (optical sensor). It will be
|
||||
/// enabled high CPI setting or so. Valid valus are 0x04 or 0x81. Define this
|
||||
/// in your config.h to be enable. Please note that using this option will
|
||||
/// increase the firmware size by more than 4KB.
|
||||
//#define KEYBALL_PMW3360_UPLOAD_SROM_ID 0x04
|
||||
//#define KEYBALL_PMW3360_UPLOAD_SROM_ID 0x81
|
||||
|
||||
/// Defining this macro keeps two functions intact: keycode_config() and
|
||||
/// mod_config() in keycode_config.c.
|
||||
///
|
||||
/// These functions customize the magic key code and are useless if the magic
|
||||
/// key code is disabled. Therefore, Keyball automatically disables it.
|
||||
/// However, there may be cases where you still need these functions even after
|
||||
/// disabling the magic key code. In that case, define this macro.
|
||||
//#define KEYBALL_KEEP_MAGIC_FUNCTIONS
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Constants
|
||||
|
||||
#define KEYBALL_TX_GETINFO_INTERVAL 500
|
||||
#define KEYBALL_TX_GETINFO_MAXTRY 10
|
||||
#define KEYBALL_TX_GETMOTION_INTERVAL 4
|
||||
|
||||
#if (PRODUCT_ID & 0xff00) == 0x0000
|
||||
# define KEYBALL_MODEL 46
|
||||
#elif (PRODUCT_ID & 0xff00) == 0x0100
|
||||
# define KEYBALL_MODEL 61
|
||||
#elif (PRODUCT_ID & 0xff00) == 0x0200
|
||||
# define KEYBALL_MODEL 39
|
||||
#elif (PRODUCT_ID & 0xff00) == 0x0300
|
||||
# define KEYBALL_MODEL 147
|
||||
#elif (PRODUCT_ID & 0xff00) == 0x0400
|
||||
# define KEYBALL_MODEL 44
|
||||
#endif
|
||||
|
||||
#define KEYBALL_OLED_MAX_PRESSING_KEYCODES 6
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Types
|
||||
|
||||
enum keyball_keycodes {
|
||||
KBC_RST = QK_KB_0, // Keyball configuration: reset to default
|
||||
KBC_SAVE = QK_KB_1, // Keyball configuration: save to EEPROM
|
||||
|
||||
CPI_I100 = QK_KB_2, // CPI +100 CPI
|
||||
CPI_D100 = QK_KB_3, // CPI -100 CPI
|
||||
CPI_I1K = QK_KB_4, // CPI +1000 CPI
|
||||
CPI_D1K = QK_KB_5, // CPI -1000 CPI
|
||||
|
||||
// In scroll mode, motion from primary trackball is treated as scroll
|
||||
// wheel.
|
||||
SCRL_TO = QK_KB_6, // Toggle scroll mode
|
||||
SCRL_MO = QK_KB_7, // Momentary scroll mode
|
||||
SCRL_DVI = QK_KB_8, // Increment scroll divider
|
||||
SCRL_DVD = QK_KB_9, // Decrement scroll divider
|
||||
|
||||
SSNP_VRT = QK_KB_13, // Set scroll snap mode as vertical
|
||||
SSNP_HOR = QK_KB_14, // Set scroll snap mode as horizontal
|
||||
SSNP_FRE = QK_KB_15, // Set scroll snap mode as disable (free scroll)
|
||||
|
||||
// Auto mouse layer control keycodes.
|
||||
// Only works when POINTING_DEVICE_AUTO_MOUSE_ENABLE is defined.
|
||||
AML_TO = QK_KB_10, // Toggle automatic mouse layer
|
||||
AML_I50 = QK_KB_11, // Increment automatic mouse layer timeout
|
||||
AML_D50 = QK_KB_12, // Decrement automatic mouse layer timeout
|
||||
|
||||
// User customizable 32 keycodes.
|
||||
KEYBALL_SAFE_RANGE = QK_USER_0,
|
||||
};
|
||||
|
||||
typedef union {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
uint8_t cpi : 7;
|
||||
uint8_t sdiv : 3; // scroll divider
|
||||
#ifdef POINTING_DEVICE_AUTO_MOUSE_ENABLE
|
||||
uint8_t amle : 1; // automatic mouse layer enabled
|
||||
uint16_t amlto : 5; // automatic mouse layer timeout
|
||||
#endif
|
||||
#if KEYBALL_SCROLLSNAP_ENABLE == 2
|
||||
uint8_t ssnap : 2; // scroll snap mode
|
||||
#endif
|
||||
};
|
||||
} keyball_config_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t ballcnt; // count of balls: support only 0 or 1, for now
|
||||
} keyball_info_t;
|
||||
|
||||
typedef struct {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
} keyball_motion_t;
|
||||
|
||||
typedef uint8_t keyball_cpi_t;
|
||||
|
||||
typedef enum {
|
||||
KEYBALL_SCROLLSNAP_MODE_VERTICAL = 0,
|
||||
KEYBALL_SCROLLSNAP_MODE_HORIZONTAL = 1,
|
||||
KEYBALL_SCROLLSNAP_MODE_FREE = 2,
|
||||
} keyball_scrollsnap_mode_t;
|
||||
|
||||
typedef struct {
|
||||
bool this_have_ball;
|
||||
bool that_enable;
|
||||
bool that_have_ball;
|
||||
|
||||
keyball_motion_t this_motion;
|
||||
keyball_motion_t that_motion;
|
||||
|
||||
uint8_t cpi_value;
|
||||
bool cpi_changed;
|
||||
|
||||
bool scroll_mode;
|
||||
uint32_t scroll_mode_changed;
|
||||
uint8_t scroll_div;
|
||||
|
||||
#if KEYBALL_SCROLLSNAP_ENABLE == 1
|
||||
uint32_t scroll_snap_last;
|
||||
int8_t scroll_snap_tension_h;
|
||||
#elif KEYBALL_SCROLLSNAP_ENABLE == 2
|
||||
keyball_scrollsnap_mode_t scrollsnap_mode;
|
||||
#endif
|
||||
|
||||
uint16_t last_kc;
|
||||
keypos_t last_pos;
|
||||
report_mouse_t last_mouse;
|
||||
|
||||
// Buffer to indicate pressing keys.
|
||||
char pressing_keys[KEYBALL_OLED_MAX_PRESSING_KEYCODES + 1];
|
||||
} keyball_t;
|
||||
|
||||
typedef enum {
|
||||
KEYBALL_ADJUST_PENDING = 0,
|
||||
KEYBALL_ADJUST_PRIMARY = 1,
|
||||
KEYBALL_ADJUST_SECONDARY = 2,
|
||||
} keyball_adjust_t;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Exported values (touch carefully)
|
||||
|
||||
extern keyball_t keyball;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Hook points
|
||||
|
||||
/// keyball_on_adjust_layout is called when the keyboard layout adjustted
|
||||
void keyball_on_adjust_layout(keyball_adjust_t v);
|
||||
|
||||
/// keyball_on_apply_motion_to_mouse_move applies trackball's motion m to r as
|
||||
/// mouse movement.
|
||||
/// You can change the default algorithm by override this function.
|
||||
void keyball_on_apply_motion_to_mouse_move(keyball_motion_t *m, report_mouse_t *r, bool is_left);
|
||||
|
||||
/// keyball_on_apply_motion_to_mouse_scroll applies trackball's motion m to r
|
||||
/// as mouse scroll.
|
||||
/// You can change the default algorithm by override this function.
|
||||
void keyball_on_apply_motion_to_mouse_scroll(keyball_motion_t *m, report_mouse_t *r, bool is_left);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Public API functions
|
||||
|
||||
/// keyball_oled_render_ballinfo renders ball information to OLED.
|
||||
/// It uses just 21 columns to show the info.
|
||||
void keyball_oled_render_ballinfo(void);
|
||||
|
||||
/// keyball_oled_render_keyinfo renders last processed key information to OLED.
|
||||
/// It shows column, row, key code, and key name (if available).
|
||||
void keyball_oled_render_keyinfo(void);
|
||||
|
||||
/// keyball_oled_render_layerinfo renders current layer status information to
|
||||
/// OLED. It shows layer mask with number (1~f) for active layers and '_' for
|
||||
/// inactive layers.
|
||||
void keyball_oled_render_layerinfo(void);
|
||||
|
||||
/// keyball_get_scroll_mode gets current scroll mode.
|
||||
bool keyball_get_scroll_mode(void);
|
||||
|
||||
/// keyball_set_scroll_mode modify scroll mode.
|
||||
void keyball_set_scroll_mode(bool mode);
|
||||
|
||||
/// keyball_get_scrollsnap_mode gets current scroll snap mode.
|
||||
keyball_scrollsnap_mode_t keyball_get_scrollsnap_mode(void);
|
||||
|
||||
/// keyball_set_scrollsnap_mode change scroll snap mode.
|
||||
void keyball_set_scrollsnap_mode(keyball_scrollsnap_mode_t mode);
|
||||
|
||||
/// keyball_get_scroll_div gets current scroll divider.
|
||||
/// See also keyball_set_scroll_div for the scroll divider's detail.
|
||||
uint8_t keyball_get_scroll_div(void);
|
||||
|
||||
/// keyball_set_scroll_div changes scroll divider.
|
||||
///
|
||||
/// The scroll divider is the number that divides the raw value when applying
|
||||
/// trackball motion to scrolling. The CPI value of the trackball is very
|
||||
/// high, so if you apply it to scrolling as is, it will scroll too much.
|
||||
/// In order to adjust the scroll amount to be appropriate, it is applied after
|
||||
/// dividing it by a scroll divider. The actual denominator is determined by
|
||||
/// the following formula:
|
||||
///
|
||||
/// denominator = 2 ^ (div - 1) ^2
|
||||
///
|
||||
/// Valid values are between 1 and 7, KEYBALL_SCROLL_DIV_DEFAULT is used when 0
|
||||
/// is specified.
|
||||
void keyball_set_scroll_div(uint8_t div);
|
||||
|
||||
/// keyball_get_cpi gets current CPI of trackball.
|
||||
/// The actual CPI value is the returned value +1 and multiplied by 100:
|
||||
///
|
||||
/// CPI = (v + 1) * 100
|
||||
uint8_t keyball_get_cpi(void);
|
||||
|
||||
/// keyball_set_cpi changes CPI of trackball.
|
||||
/// Valid values are between 0 to 119, and the actual CPI value is the set
|
||||
/// value +1 and multiplied by 100:
|
||||
///
|
||||
/// CPI = (v + 1) * 100
|
||||
///
|
||||
/// In addition, if you do not upload SROM, the maximum value will be limited
|
||||
/// to 34 (3500CPI).
|
||||
void keyball_set_cpi(uint8_t cpi);
|
52
keyboards/yowkees/lib/keyball/keycodes.md
Normal file
52
keyboards/yowkees/lib/keyball/keycodes.md
Normal file
@ -0,0 +1,52 @@
|
||||
# Keyball: Special Keycodes
|
||||
|
||||
* [English/英語](#english)
|
||||
* [日本語/Japanese](#japanese)
|
||||
|
||||
<a id="english"></a>
|
||||
## Special Keycodes
|
||||
|
||||
| Keycode | Value on Remap | Hex | Description |
|
||||
|:-----------|:----------------|:---------|:------------------------------------------------------------------|
|
||||
| `KBC_RST` | `Kb 0` | `0x7e00` | Reset Keyball configuration[^1] |
|
||||
| `KBC_SAVE` | `Kb 1` | `0x7e01` | Persist Keyball configuration[^1] to EEPROM |
|
||||
| `CPI_I100` | `Kb 2` | `0x7e02` | Increase 100 CPI (max 12000) |
|
||||
| `CPI_D100` | `Kb 3` | `0x7e03` | Decrease 100 CPI (min 100) |
|
||||
| `CPI_I1K` | `Kb 4` | `0x7e04` | Increase 1000 CPI (max 12000) |
|
||||
| `CPI_D1K` | `Kb 5` | `0x7e05` | Decrease 1000 CPI (min 100) |
|
||||
| `SCRL_TO` | `Kb 6` | `0x7e06` | Toggle scroll mode |
|
||||
| `SCRL_MO` | `Kb 7` | `0x7e07` | Enable scroll mode when pressing |
|
||||
| `SCRL_DVI` | `Kb 8` | `0x7e08` | Increase scroll divider (max D7 = 1/128) <- Most Scroll slow |
|
||||
| `SCRL_DVD` | `Kb 9` | `0x7e09` | Decrease scroll divider (min 0 = 1/1) <- Most Scroll fast |
|
||||
| `AML_TO` | `Kb 10` | `0x7e0a` | Toggle automatic mouse layer |
|
||||
| `AML_I50` | `Kb 11` | `0x7e0b` | Increase 50ms automatic mouse layer timeout (max 1000ms) |
|
||||
| `AML_D50` | `Kb 12` | `0x7e0c` | Decrease 50ms automatic mouse layer timeout (min 100ms) |
|
||||
| `SSNP_VRT` | `Kb 13` | `0x7e0d` | Set scroll snap mode as vertical |
|
||||
| `SSNP_HOR` | `Kb 14` | `0x7e0e` | Set scroll snap mode as horizontal |
|
||||
| `SSNP_FRE` | `Kb 15` | `0x7e0f` | Set scroll snap mode as disable (free scroll) |
|
||||
|
||||
[^1]: CPI, scroll divider, automatic mouse layer's enable/disable, and automatic mouse layer's timeout.
|
||||
|
||||
<a id="japanese"></a>
|
||||
## 特殊キーコード
|
||||
|
||||
| キーコード | Remap上での表記 | 値 | 説明 |
|
||||
|:-----------|:----------------|:---------|:------------------------------------------------------------------|
|
||||
| `KBC_RST` | `Kb 0` | `0x7e00` | Keyball設定[^2]のリセット |
|
||||
| `KBC_SAVE` | `Kb 1` | `0x7e01` | 現在のKeyball設定[^2]をEEPROMに保存します |
|
||||
| `CPI_I100` | `Kb 2` | `0x7e02` | CPIを100増加させます(最大:12000) |
|
||||
| `CPI_D100` | `Kb 3` | `0x7e03` | CPIを100減少させます(最小:100) |
|
||||
| `CPI_I1K` | `Kb 4` | `0x7e04` | CPIを1000増加させます(最大:12000) |
|
||||
| `CPI_D1K` | `Kb 5` | `0x7e05` | CPIを1000減少させます(最小:100) |
|
||||
| `SCRL_TO` | `Kb 6` | `0x7e06` | タップごとにスクロールモードのON/OFFを切り替えます |
|
||||
| `SCRL_MO` | `Kb 7` | `0x7e07` | キーを押している間、スクロールモードになります |
|
||||
| `SCRL_DVI` | `Kb 8` | `0x7e08` | スクロール除数を1つ上げます(max D7 = 1/128)←最もスクロール遅い |
|
||||
| `SCRL_DVD` | `Kb 9` | `0x7e09` | スクロール除数を1つ下げます(min D0 = 1/1)←最もスクロール速い |
|
||||
| `AML_TO` | `Kb 10` | `0x7e0a` | 自動マウスレイヤーをトグルします。 |
|
||||
| `AML_I50` | `Kb 11` | `0x7e0b` | 自動マウスレイヤーのタイムアウトを50msec増やします (max 1000ms) |
|
||||
| `AML_D50` | `Kb 12` | `0x7e0c` | 自動マウスレイヤーのタイムアウトを50msec減らします (min 100ms) |
|
||||
| `SSNP_VRT` | `Kb 13` | `0x7e0d` | スクロールスナップモードを垂直にする |
|
||||
| `SSNP_HOR` | `Kb 14` | `0x7e0e` | スクロールスナップモードを水平にする |
|
||||
| `SSNP_FRE` | `Kb 15` | `0x7e0f` | スクロールスナップモードを無効にする(自由スクロール) |
|
||||
|
||||
[^2]: CPI、スクロール除数、自動マウスレイヤーのON/OFF状態、及び自動マウスレイヤのタイムアウト
|
212
keyboards/yowkees/lib/logofont/font.md
Normal file
212
keyboards/yowkees/lib/logofont/font.md
Normal file
@ -0,0 +1,212 @@
|
||||
# Special character patterns
|
||||
|
||||
## blank
|
||||
|
||||
```
|
||||
00000000
|
||||
00000000
|
||||
00000000
|
||||
00000000
|
||||
00000000
|
||||
00000000
|
||||
```
|
||||
|
||||
## blank indicator
|
||||
|
||||
```
|
||||
00000000
|
||||
00010000
|
||||
00010000
|
||||
00010000
|
||||
00000000
|
||||
00000000
|
||||
00000000
|
||||
```
|
||||
|
||||
## vertical label separator
|
||||
|
||||
```
|
||||
00000000
|
||||
00000000
|
||||
01010101
|
||||
00000000
|
||||
00000000
|
||||
00000000
|
||||
```
|
||||
|
||||
## "ON" in 2 chars with light background
|
||||
|
||||
```
|
||||
00111110
|
||||
01100011
|
||||
01011101
|
||||
01011101
|
||||
01100011
|
||||
01111111
|
||||
01000001
|
||||
01111011
|
||||
01110111
|
||||
01000001
|
||||
00111110
|
||||
00000000
|
||||
```
|
||||
|
||||
## "OFF" in 2 chars
|
||||
|
||||
```
|
||||
00000000
|
||||
00011100
|
||||
00100010
|
||||
00100010
|
||||
00011100
|
||||
00000000
|
||||
00111110
|
||||
00001010
|
||||
00000000
|
||||
00111110
|
||||
00001010
|
||||
00000000
|
||||
```
|
||||
|
||||
## "aye" in 2 chars for "Layer"
|
||||
|
||||
```
|
||||
00110010
|
||||
00101010
|
||||
00111100
|
||||
00000000
|
||||
00100110
|
||||
00101000
|
||||
00011110
|
||||
00000000
|
||||
00011100
|
||||
00101010
|
||||
00101100
|
||||
00000000
|
||||
```
|
||||
|
||||
## right half 'R'
|
||||
|
||||
```
|
||||
00000000
|
||||
00000000
|
||||
00111111
|
||||
00001001
|
||||
00110110
|
||||
00000000
|
||||
```
|
||||
|
||||
## right half 'C'
|
||||
|
||||
```
|
||||
00000000
|
||||
00000000
|
||||
00011110
|
||||
00100001
|
||||
00100001
|
||||
00000000
|
||||
```
|
||||
|
||||
## "KC" in 2 chars, indicate "keycode"
|
||||
|
||||
```
|
||||
00000000
|
||||
00000000
|
||||
00000000
|
||||
00000000
|
||||
00111111
|
||||
00001000
|
||||
00110111
|
||||
00000000
|
||||
00011110
|
||||
00100001
|
||||
00100001
|
||||
00000000
|
||||
```
|
||||
|
||||
## "CPI" in 2 chars
|
||||
|
||||
```
|
||||
00011110
|
||||
00100001
|
||||
00100001
|
||||
00000000
|
||||
00111111
|
||||
00001001
|
||||
00000110
|
||||
00000000
|
||||
00100001
|
||||
00111111
|
||||
00100001
|
||||
00000000
|
||||
```
|
||||
|
||||
## "SCR" in 2 chars
|
||||
|
||||
```
|
||||
00100010
|
||||
00100101
|
||||
00011001
|
||||
00000000
|
||||
00011110
|
||||
00100001
|
||||
00100001
|
||||
00000000
|
||||
00111111
|
||||
00001001
|
||||
00110110
|
||||
00000000
|
||||
```
|
||||
|
||||
## "DIV" in 2 chars
|
||||
|
||||
```
|
||||
00111111
|
||||
00100001
|
||||
00011110
|
||||
00000000
|
||||
00100001
|
||||
00111111
|
||||
00100001
|
||||
00000000
|
||||
00001111
|
||||
00110000
|
||||
00001111
|
||||
00000000
|
||||
```
|
||||
|
||||
## "AML" in 2 chars
|
||||
|
||||
```
|
||||
00111110
|
||||
00001001
|
||||
00111110
|
||||
00000000
|
||||
00111111
|
||||
00000110
|
||||
00111111
|
||||
00000000
|
||||
00111111
|
||||
00100000
|
||||
00100000
|
||||
00000000
|
||||
```
|
||||
|
||||
## inactive "ON" in 2 chars
|
||||
|
||||
(not use)
|
||||
|
||||
```
|
||||
00000000
|
||||
00011100
|
||||
00100010
|
||||
00100010
|
||||
00011100
|
||||
00000000
|
||||
00111110
|
||||
00000100
|
||||
00001000
|
||||
00111110
|
||||
00000000
|
||||
00000000
|
||||
```
|
199
keyboards/yowkees/lib/logofont/logofont.c
Normal file
199
keyboards/yowkees/lib/logofont/logofont.c
Normal file
@ -0,0 +1,199 @@
|
||||
// Copyright 2021 @Yowkees: Keyball logo
|
||||
//
|
||||
// - Keyball logo (0x80 ~ 0xAF)
|
||||
//
|
||||
// Copyright 2024 MURAOKA Taro (aka KoRoN, @kaoriya): Other glyphs
|
||||
//
|
||||
// - ASCII characters (0x20 ~ 0x7E)
|
||||
// - Special characters (0xB0 ~ 0xC3)
|
||||
|
||||
#include "progmem.h"
|
||||
|
||||
// clang-format off
|
||||
const unsigned char font[] PROGMEM = {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Standard ASCII characters, most of which fit within 5x6 area.
|
||||
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20 ' '
|
||||
0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, // 0x21 '!'
|
||||
0x00, 0x07, 0x00, 0x07, 0x00, 0x00, // 0x22 '"'
|
||||
0x12, 0x3F, 0x12, 0x3F, 0x12, 0x00, // 0x23 '#'
|
||||
0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x00, // 0x24 '$'
|
||||
0x36, 0x09, 0x1E, 0x24, 0x1B, 0x00, // 0x25 '%'
|
||||
0x18, 0x26, 0x29, 0x12, 0x28, 0x00, // 0x26 '&'
|
||||
0x00, 0x00, 0x07, 0x00, 0x00, 0x00, // 0x27 '''
|
||||
0x00, 0x1E, 0x21, 0x40, 0x00, 0x00, // 0x28 '('
|
||||
0x00, 0x40, 0x21, 0x1E, 0x00, 0x00, // 0x29 ')'
|
||||
0x14, 0x08, 0x3E, 0x08, 0x14, 0x00, // 0x2A '*'
|
||||
0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, // 0x2B '+'
|
||||
0x00, 0x00, 0x30, 0x70, 0x00, 0x00, // 0x2C ','
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x00, // 0x2D '-'
|
||||
0x00, 0x00, 0x30, 0x30, 0x00, 0x00, // 0x2E '.'
|
||||
0x20, 0x10, 0x0C, 0x02, 0x01, 0x00, // 0x2F '/'
|
||||
0x1E, 0x29, 0x25, 0x23, 0x1E, 0x00, // 0x30 '0'
|
||||
0x00, 0x22, 0x3F, 0x20, 0x00, 0x00, // 0x31 '1'
|
||||
0x22, 0x31, 0x29, 0x29, 0x26, 0x00, // 0x32 '2'
|
||||
0x12, 0x21, 0x25, 0x25, 0x1A, 0x00, // 0x33 '3'
|
||||
0x18, 0x14, 0x12, 0x3F, 0x10, 0x00, // 0x34 '4'
|
||||
0x17, 0x25, 0x25, 0x25, 0x18, 0x00, // 0x35 '5'
|
||||
0x1E, 0x25, 0x25, 0x25, 0x18, 0x00, // 0x36 '6'
|
||||
0x01, 0x31, 0x09, 0x05, 0x03, 0x00, // 0x37 '7'
|
||||
0x1A, 0x25, 0x25, 0x25, 0x1A, 0x00, // 0x38 '8'
|
||||
0x06, 0x29, 0x29, 0x29, 0x1E, 0x00, // 0x39 '9'
|
||||
0x00, 0x00, 0x33, 0x33, 0x00, 0x00, // 0x3A ':'
|
||||
0x00, 0x00, 0x33, 0x73, 0x00, 0x00, // 0x3B ';'
|
||||
0x08, 0x14, 0x14, 0x22, 0x22, 0x00, // 0x3C '<'
|
||||
0x14, 0x14, 0x14, 0x14, 0x14, 0x00, // 0x3D '='
|
||||
0x22, 0x22, 0x14, 0x14, 0x08, 0x00, // 0x3E '>'
|
||||
0x02, 0x01, 0x29, 0x05, 0x02, 0x00, // 0x3F '?'
|
||||
0x3E, 0x59, 0x55, 0x5D, 0x3E, 0x00, // 0x40 '@'
|
||||
0x38, 0x16, 0x11, 0x16, 0x38, 0x00, // 0x41 'A'
|
||||
0x3F, 0x25, 0x25, 0x25, 0x1A, 0x00, // 0x42 'B'
|
||||
0x1E, 0x21, 0x21, 0x21, 0x12, 0x00, // 0x43 'C'
|
||||
0x3F, 0x21, 0x21, 0x21, 0x1E, 0x00, // 0x44 'D'
|
||||
0x3F, 0x25, 0x25, 0x25, 0x21, 0x00, // 0x45 'E'
|
||||
0x3F, 0x09, 0x09, 0x09, 0x01, 0x00, // 0x46 'F'
|
||||
0x1E, 0x21, 0x21, 0x29, 0x1A, 0x00, // 0x47 'G'
|
||||
0x3F, 0x04, 0x04, 0x04, 0x3F, 0x00, // 0x48 'H'
|
||||
0x00, 0x21, 0x3F, 0x21, 0x00, 0x00, // 0x49 'I'
|
||||
0x10, 0x20, 0x20, 0x20, 0x1F, 0x00, // 0x4A 'J'
|
||||
0x3F, 0x04, 0x0C, 0x12, 0x21, 0x00, // 0x4B 'K'
|
||||
0x3F, 0x20, 0x20, 0x20, 0x20, 0x00, // 0x4C 'L'
|
||||
0x3F, 0x02, 0x04, 0x02, 0x3F, 0x00, // 0x4D 'M'
|
||||
0x3F, 0x02, 0x0C, 0x10, 0x3F, 0x00, // 0x4E 'N'
|
||||
0x1E, 0x21, 0x21, 0x21, 0x1E, 0x00, // 0x4F 'O'
|
||||
0x3F, 0x09, 0x09, 0x09, 0x06, 0x00, // 0x50 'P'
|
||||
0x1E, 0x21, 0x29, 0x11, 0x2E, 0x00, // 0x51 'Q'
|
||||
0x3F, 0x09, 0x09, 0x19, 0x26, 0x00, // 0x52 'R'
|
||||
0x12, 0x25, 0x25, 0x29, 0x12, 0x00, // 0x53 'S'
|
||||
0x01, 0x01, 0x3F, 0x01, 0x01, 0x00, // 0x54 'T'
|
||||
0x1F, 0x20, 0x20, 0x20, 0x1F, 0x00, // 0x55 'U'
|
||||
0x07, 0x18, 0x20, 0x18, 0x07, 0x00, // 0x56 'V'
|
||||
0x0F, 0x30, 0x0C, 0x30, 0x0F, 0x00, // 0x57 'W'
|
||||
0x21, 0x12, 0x0C, 0x12, 0x21, 0x00, // 0x58 'X'
|
||||
0x03, 0x04, 0x38, 0x04, 0x03, 0x00, // 0x59 'Y'
|
||||
0x31, 0x29, 0x25, 0x23, 0x21, 0x00, // 0x5A 'Z'
|
||||
0x00, 0x7F, 0x41, 0x41, 0x00, 0x00, // 0x5B '['
|
||||
0x01, 0x02, 0x0C, 0x10, 0x20, 0x00, // 0x5C '\'
|
||||
0x00, 0x41, 0x41, 0x7F, 0x00, 0x00, // 0x5D ']'
|
||||
0x04, 0x02, 0x01, 0x02, 0x04, 0x00, // 0x5E '^'
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x00, // 0x5F '_'
|
||||
0x00, 0x01, 0x03, 0x04, 0x00, 0x00, // 0x60 '`'
|
||||
0x10, 0x2A, 0x2A, 0x2A, 0x3C, 0x00, // 0x61 'a'
|
||||
0x3F, 0x24, 0x22, 0x22, 0x1C, 0x00, // 0x62 'b'
|
||||
0x1C, 0x22, 0x22, 0x22, 0x24, 0x00, // 0x63 'c'
|
||||
0x1C, 0x22, 0x22, 0x24, 0x3F, 0x00, // 0x64 'd'
|
||||
0x1C, 0x2A, 0x2A, 0x2A, 0x0C, 0x00, // 0x65 'e'
|
||||
0x00, 0x04, 0x3E, 0x05, 0x05, 0x00, // 0x66 'f'
|
||||
0x0C, 0x52, 0x52, 0x54, 0x3E, 0x00, // 0x67 'g'
|
||||
0x3F, 0x08, 0x04, 0x04, 0x38, 0x00, // 0x68 'h'
|
||||
0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, // 0x69 'i'
|
||||
0x20, 0x40, 0x40, 0x3D, 0x00, 0x00, // 0x6A 'j'
|
||||
0x3F, 0x08, 0x08, 0x14, 0x22, 0x00, // 0x6B 'k'
|
||||
0x00, 0x01, 0x3F, 0x20, 0x00, 0x00, // 0x6C 'l'
|
||||
0x3E, 0x02, 0x3C, 0x02, 0x3C, 0x00, // 0x6D 'm'
|
||||
0x3E, 0x04, 0x02, 0x02, 0x3C, 0x00, // 0x6E 'n'
|
||||
0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, // 0x6F 'o'
|
||||
0x7E, 0x14, 0x22, 0x22, 0x1C, 0x00, // 0x70 'p'
|
||||
0x1C, 0x22, 0x22, 0x14, 0x7E, 0x00, // 0x71 'q'
|
||||
0x3E, 0x04, 0x02, 0x02, 0x04, 0x00, // 0x72 'r'
|
||||
0x24, 0x2A, 0x2A, 0x2A, 0x12, 0x00, // 0x73 's'
|
||||
0x00, 0x02, 0x1F, 0x22, 0x00, 0x00, // 0x74 't'
|
||||
0x1E, 0x20, 0x20, 0x10, 0x3E, 0x00, // 0x75 'u'
|
||||
0x06, 0x18, 0x20, 0x18, 0x06, 0x00, // 0x76 'v'
|
||||
0x0E, 0x30, 0x0C, 0x30, 0x0E, 0x00, // 0x77 'w'
|
||||
0x22, 0x14, 0x08, 0x14, 0x22, 0x00, // 0x78 'x'
|
||||
0x0E, 0x50, 0x50, 0x48, 0x3E, 0x00, // 0x79 'y'
|
||||
0x22, 0x32, 0x2A, 0x26, 0x22, 0x00, // 0x7A 'z'
|
||||
0x08, 0x08, 0x77, 0x41, 0x41, 0x00, // 0x7B '{'
|
||||
0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, // 0x7C '|'
|
||||
0x41, 0x41, 0x77, 0x08, 0x08, 0x00, // 0x7D '}'
|
||||
0x18, 0x04, 0x08, 0x10, 0x0C, 0x00, // 0x7E '~'
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x7F
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Keyball logo
|
||||
|
||||
// Logo 1/3 (0x80 ~ 0x8F)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xC0, 0xF0, 0xF8, 0x8C, 0x86, 0xC6,
|
||||
0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFE, 0xFC, 0xFC, 0xF8, 0xE0,
|
||||
0x80, 0x00, 0x00, 0x00, 0xF0, 0xF0,
|
||||
0xF0, 0x00, 0x00, 0xC0, 0xE0, 0xF0,
|
||||
0x70, 0x70, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xF8, 0xF8, 0xF8, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xF8, 0xF8,
|
||||
0xF8, 0x00, 0x00, 0xF8, 0xF8, 0xF8,
|
||||
|
||||
// Logo 2/3 (0x90 ~ 0x9F)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
0xE0, 0x70, 0x78, 0x68, 0x6C, 0x64,
|
||||
0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7,
|
||||
0xE7, 0x3F, 0x1F, 0x0F, 0x3F, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F,
|
||||
0x0F, 0x00, 0x00, 0x00, 0xFF, 0xFF,
|
||||
0xFF, 0x1E, 0x3F, 0xFF, 0xF3, 0xE1,
|
||||
0x80, 0x00, 0x00, 0xF8, 0xFC, 0xFE,
|
||||
0x36, 0x36, 0x36, 0x3E, 0xBC, 0xB8,
|
||||
0x00, 0x0E, 0x3E, 0xFE, 0xF0, 0x80,
|
||||
0xF0, 0xFE, 0x3E, 0x0E, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0x06, 0x06, 0x06,
|
||||
0xFE, 0xFE, 0xFC, 0x70, 0x00, 0xE6,
|
||||
0xF6, 0xF6, 0x36, 0x36, 0xFE, 0xFE,
|
||||
0xFC, 0x00, 0x00, 0x00, 0xFF, 0xFF,
|
||||
0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
|
||||
|
||||
// Logo 3/3 (0xA0 ~ 0xAF)
|
||||
0x00, 0x00, 0x00, 0x00, 0x3C, 0x7F,
|
||||
0x63, 0x60, 0xE0, 0xC0, 0xC0, 0xC0,
|
||||
0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF,
|
||||
0xFF, 0xC0, 0x60, 0x30, 0x18, 0x0F,
|
||||
0x03, 0x03, 0x03, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x03, 0x03,
|
||||
0x03, 0x00, 0x00, 0x00, 0x03, 0x03,
|
||||
0x03, 0x03, 0x00, 0x01, 0x03, 0x03,
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x01,
|
||||
0x00, 0x70, 0x70, 0x39, 0x3F, 0x1F,
|
||||
0x07, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x03, 0x03, 0x01, 0x00, 0x00, 0x01,
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x01, 0x00, 0x00, 0x00, 0x03, 0x03,
|
||||
0x03, 0x00, 0x00, 0x03, 0x03, 0x03,
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Special characters for Keyball
|
||||
|
||||
// 0xB0
|
||||
0x00, 0x10, 0x10, 0x10, 0x00, 0x00, // B0: blank indicator (BL)
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x00, // B1: vertical label separator
|
||||
0x3E, 0x63, 0x5D, 0x5D, 0x63, 0x7F, // B2, B3:
|
||||
0x41, 0x7B, 0x77, 0x41, 0x3E, 0x00, // "ON" in 2 chars with light bg.
|
||||
0x00, 0x1C, 0x22, 0x22, 0x1C, 0x00, // B4, B5:
|
||||
0x3E, 0x0A, 0x00, 0x3E, 0x0A, 0x00, // "OFF" in 2 chars
|
||||
0x32, 0x2A, 0x3C, 0x00, 0x26, 0x28, // B6, B7:
|
||||
0x1E, 0x00, 0x1C, 0x2A, 0x2C, 0x00, // "aye" in 2 chars
|
||||
0x00, 0x00, 0x3F, 0x09, 0x36, 0x00, // B8: right half "R" indicate "row"
|
||||
0x00, 0x00, 0x1E, 0x21, 0x21, 0x00, // B9: right halc "C" indicate "column"
|
||||
0x00, 0x00, 0x00, 0x00, 0x3F, 0x08, // BA, BB:
|
||||
0x37, 0x00, 0x1E, 0x21, 0x21, 0x00, // "KC" in 2 chars right aligned
|
||||
0x1E, 0x21, 0x21, 0x00, 0x3F, 0x09, // BC, BD:
|
||||
0x06, 0x00, 0x21, 0x3F, 0x21, 0x00, // "CPI" in 2 chars
|
||||
0x22, 0x25, 0x19, 0x00, 0x1E, 0x21, // BE, BF:
|
||||
0x21, 0x00, 0x3F, 0x09, 0x36, 0x00, // "SCR" in 2 chars indicate "scroll"
|
||||
|
||||
// 0xC0
|
||||
0x3F, 0x21, 0x1E, 0x00, 0x21, 0x3F, // C0, C1:
|
||||
0x21, 0x00, 0x0F, 0x30, 0x0F, 0x00, // "DIV" in 2 chars indicate "divider"
|
||||
0x3E, 0x09, 0x3E, 0x00, 0x3F, 0x06, // C2, C3:
|
||||
0x3F, 0x00, 0x3F, 0x20, 0x20, 0x00, // "AML" in 2 chars
|
||||
};
|
||||
// clang-format on
|
61
keyboards/yowkees/lib/oledkit/oledkit.c
Normal file
61
keyboards/yowkees/lib/oledkit/oledkit.c
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
Copyright 2021 @Yowkees
|
||||
Copyright 2021 MURAOKA Taro (aka KoRoN, @kaoriya)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "quantum.h"
|
||||
|
||||
#if defined(OLED_ENABLE) && !defined(OLEDKIT_DISABLE)
|
||||
|
||||
__attribute__((weak)) void oledkit_render_logo_user(void) {
|
||||
// Require `OLED_FONT_H "keyboards/keyball/lib/logofont/logofont.c"`
|
||||
char ch = 0x80;
|
||||
for (int y = 0; y < 3; y++) {
|
||||
oled_write_P(PSTR(" "), false);
|
||||
for (int x = 0; x < 16; x++) {
|
||||
oled_write_char(ch++, false);
|
||||
}
|
||||
oled_advance_page(false);
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((weak)) void oledkit_render_info_user(void) {
|
||||
oledkit_render_logo_user();
|
||||
}
|
||||
|
||||
__attribute__((weak)) bool oled_task_user(void) {
|
||||
if (is_keyboard_master()) {
|
||||
oledkit_render_info_user();
|
||||
} else {
|
||||
oledkit_render_logo_user();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
__attribute__((weak)) oled_rotation_t oled_init_user(oled_rotation_t rotation) {
|
||||
// Logo needs to be rotated 180 degrees.
|
||||
//
|
||||
// A typical OLED has a narrow margin on the left side near the origin, and
|
||||
// a wide margin on the right side. The Keyball logo consists of three
|
||||
// lines. If the logo is displayed on an OLED consisting of four lines, the
|
||||
// margin on the right side will be too large and the balance is not good.
|
||||
//
|
||||
// Additionally, by rotating it, the left side of the logo will be above
|
||||
// the OLED screen, giving it a natural look.
|
||||
return !is_keyboard_master() ? OLED_ROTATION_180 : rotation;
|
||||
}
|
||||
|
||||
#endif // OLED_ENABLE
|
34
keyboards/yowkees/lib/oledkit/oledkit.h
Normal file
34
keyboards/yowkees/lib/oledkit/oledkit.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
Copyright 2021 @Yowkees
|
||||
Copyright 2021 MURAOKA Taro (aka KoRoN, @kaoriya)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(OLED_ENABLE) && !defined(OLEDKIT_DISABLE)
|
||||
|
||||
// oledkit_render_info_user renders keyboard's internal state information to
|
||||
// primary board. A keymap can override this by defining a function with same
|
||||
// signature.
|
||||
//
|
||||
// It render a logo as default.
|
||||
void oledkit_render_info_user(void);
|
||||
|
||||
// oledkit_render_logo_user renders a logo of keyboard to secondary board.
|
||||
// A keymap can override this by defining a function with same signature.
|
||||
void oledkit_render_logo_user(void);
|
||||
|
||||
#endif // OLED_ENABLE
|
@ -8,7 +8,7 @@ from pathlib import Path
|
||||
from dotty_dict import dotty
|
||||
|
||||
from milc import cli
|
||||
from milc.questions import choice, question
|
||||
from milc.questions import choice, question, yesno
|
||||
|
||||
from qmk.git import git_get_username
|
||||
from qmk.json_schema import load_jsonschema
|
||||
@ -131,60 +131,70 @@ def _question(*args, **kwargs):
|
||||
return ret
|
||||
|
||||
|
||||
def prompt_keyboard():
|
||||
prompt = """{fg_yellow}Name Your Keyboard Project{style_reset_all}
|
||||
For more infomation, see:
|
||||
https://docs.qmk.fm/hardware_keyboard_guidelines#naming-your-keyboard-project
|
||||
def prompt_heading_subheading(heading, subheading):
|
||||
cli.log.info(f"{{fg_yellow}}{heading}{{style_reset_all}}")
|
||||
cli.log.info(subheading)
|
||||
|
||||
Keyboard Name? """
|
||||
|
||||
def prompt_keyboard():
|
||||
prompt_heading_subheading("Name Your Keyboard Project", """For more information, see:
|
||||
https://docs.qmk.fm/hardware_keyboard_guidelines#naming-your-keyboard-project""")
|
||||
|
||||
errmsg = 'Keyboard already exists! Please choose a different name:'
|
||||
|
||||
return _question(prompt, reprompt=errmsg, validate=lambda x: not keyboard(x).exists())
|
||||
return _question("Keyboard Name?", reprompt=errmsg, validate=lambda x: not keyboard(x).exists())
|
||||
|
||||
|
||||
def prompt_user():
|
||||
prompt = """
|
||||
{fg_yellow}Attribution{style_reset_all}
|
||||
Used for maintainer, copyright, etc
|
||||
prompt_heading_subheading("Attribution", "Used for maintainer, copyright, etc.")
|
||||
|
||||
Your GitHub Username? """
|
||||
return question(prompt, default=git_get_username())
|
||||
return question("Your GitHub Username?", default=git_get_username())
|
||||
|
||||
|
||||
def prompt_name(def_name):
|
||||
prompt = """
|
||||
{fg_yellow}More Attribution{style_reset_all}
|
||||
Used for maintainer, copyright, etc
|
||||
prompt_heading_subheading("More Attribution", "Used for maintainer, copyright, etc.")
|
||||
|
||||
Your Real Name? """
|
||||
return question(prompt, default=def_name)
|
||||
return question("Your Real Name?", default=def_name)
|
||||
|
||||
|
||||
def prompt_layout():
|
||||
prompt = """
|
||||
{fg_yellow}Pick Base Layout{style_reset_all}
|
||||
As a starting point, one of the common layouts can be used to bootstrap the process
|
||||
prompt_heading_subheading("Pick Base Layout", """As a starting point, one of the common layouts can be used to
|
||||
bootstrap the process""")
|
||||
|
||||
Default Layout? """
|
||||
# avoid overwhelming user - remove some?
|
||||
filtered_layouts = [x for x in available_layouts if not any(xs in x for xs in ['_split', '_blocker', '_tsangan', '_f13'])]
|
||||
filtered_layouts.append("none of the above")
|
||||
|
||||
return choice(prompt, filtered_layouts, default=len(filtered_layouts) - 1)
|
||||
return choice("Default Layout?", filtered_layouts, default=len(filtered_layouts) - 1)
|
||||
|
||||
|
||||
def prompt_mcu_type():
|
||||
prompt_heading_subheading(
|
||||
"What Powers Your Project", """Is your board using a separate development board, such as a Pro Micro,
|
||||
or is the microcontroller integrated onto the PCB?
|
||||
|
||||
For more information, see:
|
||||
https://docs.qmk.fm/compatible_microcontrollers"""
|
||||
)
|
||||
|
||||
return yesno("Using a Development Board?")
|
||||
|
||||
|
||||
def prompt_dev_board():
|
||||
prompt_heading_subheading("Select Development Board", """For more information, see:
|
||||
https://docs.qmk.fm/compatible_microcontrollers""")
|
||||
|
||||
return choice("Development Board?", dev_boards, default=dev_boards.index("promicro"))
|
||||
|
||||
|
||||
def prompt_mcu():
|
||||
prompt = """
|
||||
{fg_yellow}What Powers Your Project{style_reset_all}
|
||||
For more infomation, see:
|
||||
https://docs.qmk.fm/#/compatible_microcontrollers
|
||||
prompt_heading_subheading("Select Microcontroller", """For more information, see:
|
||||
https://docs.qmk.fm/compatible_microcontrollers""")
|
||||
|
||||
MCU? """
|
||||
# remove any options strictly used for compatibility
|
||||
filtered_mcu = [x for x in (dev_boards + mcu_types) if not any(xs in x for xs in ['cortex', 'unknown'])]
|
||||
filtered_mcu = [x for x in mcu_types if not any(xs in x for xs in ['cortex', 'unknown'])]
|
||||
|
||||
return choice(prompt, filtered_mcu, default=filtered_mcu.index("atmega32u4"))
|
||||
return choice("Microcontroller?", filtered_mcu, default=filtered_mcu.index("atmega32u4"))
|
||||
|
||||
|
||||
@cli.argument('-kb', '--keyboard', help='Specify the name for the new keyboard directory', arg_only=True, type=keyboard_name)
|
||||
@ -211,7 +221,11 @@ def new_keyboard(cli):
|
||||
user_name = cli.config.new_keyboard.name if cli.config.new_keyboard.name else prompt_user()
|
||||
real_name = cli.args.realname or cli.config.new_keyboard.name if cli.args.realname or cli.config.new_keyboard.name else prompt_name(user_name)
|
||||
default_layout = cli.args.layout if cli.args.layout else prompt_layout()
|
||||
mcu = cli.args.type if cli.args.type else prompt_mcu()
|
||||
|
||||
if cli.args.type:
|
||||
mcu = cli.args.type
|
||||
else:
|
||||
mcu = prompt_dev_board() if prompt_mcu_type() else prompt_mcu()
|
||||
|
||||
config = {}
|
||||
if mcu in dev_boards:
|
||||
|
@ -1,5 +1,6 @@
|
||||
"""This script automates the copying of the default keymap into your own keymap.
|
||||
"""
|
||||
import re
|
||||
import shutil
|
||||
|
||||
from milc import cli
|
||||
@ -13,6 +14,13 @@ from qmk.keyboard import keyboard_completer, keyboard_folder
|
||||
from qmk.userspace import UserspaceDefs
|
||||
|
||||
|
||||
def validate_keymap_name(name):
|
||||
"""Returns True if the given keymap name contains only a-z, 0-9 and underscore characters.
|
||||
"""
|
||||
regex = re.compile(r'^[a-zA-Z0-9][a-zA-Z0-9_]+$')
|
||||
return bool(regex.match(name))
|
||||
|
||||
|
||||
def prompt_keyboard():
|
||||
prompt = """{fg_yellow}Select Keyboard{style_reset_all}
|
||||
If you`re unsure you can view a full list of supported keyboards with {fg_yellow}qmk list-keyboards{style_reset_all}.
|
||||
@ -60,6 +68,10 @@ def new_keymap(cli):
|
||||
cli.log.error(f'Default keymap {{fg_cyan}}{keymap_path_default}{{fg_reset}} does not exist!')
|
||||
return False
|
||||
|
||||
if not validate_keymap_name(user_name):
|
||||
cli.log.error('Keymap names must contain only {fg_cyan}a-z{fg_reset}, {fg_cyan}0-9{fg_reset} and {fg_cyan}_{fg_reset}! Please choose a different name.')
|
||||
return False
|
||||
|
||||
if keymap_path_new.exists():
|
||||
cli.log.error(f'Keymap {{fg_cyan}}{user_name}{{fg_reset}} already exists! Please choose a different name.')
|
||||
return False
|
||||
|
@ -60,9 +60,7 @@ def painter_convert_graphics(cli):
|
||||
return
|
||||
|
||||
# Work out the text substitutions for rendering the output data
|
||||
args_str = " ".join((f"--{arg} {getattr(cli.args, arg.replace('-', '_'))}" for arg in ["input", "output", "format", "no-rle", "no-deltas"]))
|
||||
command = f"qmk painter-convert-graphics {args_str}"
|
||||
subs = generate_subs(cli, out_bytes, image_metadata=metadata, command=command)
|
||||
subs = generate_subs(cli, out_bytes, image_metadata=metadata, command_name="painter_convert_graphics")
|
||||
|
||||
# Render and write the header file
|
||||
header_text = render_header(subs)
|
||||
|
@ -61,10 +61,8 @@ def painter_convert_font_image(cli):
|
||||
return
|
||||
|
||||
# Work out the text substitutions for rendering the output data
|
||||
args_str = " ".join((f"--{arg} {getattr(cli.args, arg.replace('-', '_'))}" for arg in ["input", "output", "no-ascii", "unicode-glyphs", "format", "no-rle"]))
|
||||
command = f"qmk painter-convert-font-image {args_str}"
|
||||
metadata = {"glyphs": _generate_font_glyphs_list(not cli.args.no_ascii, cli.args.unicode_glyphs)}
|
||||
subs = generate_subs(cli, out_bytes, font_metadata=metadata, command=command)
|
||||
subs = generate_subs(cli, out_bytes, font_metadata=metadata, command_name="painter_convert_font_image")
|
||||
|
||||
# Render and write the header file
|
||||
header_text = render_header(subs)
|
||||
|
@ -29,6 +29,7 @@ def _convert_macros(via_macros):
|
||||
if len(via_macros) == 0:
|
||||
return list()
|
||||
split_regex = re.compile(r'(}\,)|(\,{)')
|
||||
macro_group_regex = re.compile(r'({.+?})')
|
||||
macros = list()
|
||||
for via_macro in via_macros:
|
||||
# Split VIA macro to its elements
|
||||
@ -38,13 +39,28 @@ def _convert_macros(via_macros):
|
||||
macro_data = list()
|
||||
for m in macro:
|
||||
if '{' in m or '}' in m:
|
||||
# Found keycode(s)
|
||||
keycodes = m.split(',')
|
||||
# Remove whitespaces and curly braces from around keycodes
|
||||
keycodes = list(map(lambda s: s.strip(' {}'), keycodes))
|
||||
# Remove the KC prefix
|
||||
keycodes = list(map(lambda s: s.replace('KC_', ''), keycodes))
|
||||
macro_data.append({"action": "tap", "keycodes": keycodes})
|
||||
# Split macro groups
|
||||
macro_groups = macro_group_regex.findall(m)
|
||||
for macro_group in macro_groups:
|
||||
# Remove whitespaces and curly braces from around group
|
||||
macro_group = macro_group.strip(' {}')
|
||||
|
||||
macro_action = 'tap'
|
||||
macro_keycodes = []
|
||||
|
||||
if macro_group[0] == '+':
|
||||
macro_action = 'down'
|
||||
macro_keycodes.append(macro_group[1:])
|
||||
elif macro_group[0] == '-':
|
||||
macro_action = 'up'
|
||||
macro_keycodes.append(macro_group[1:])
|
||||
else:
|
||||
macro_keycodes.extend(macro_group.split(',') if ',' in macro_group else [macro_group])
|
||||
|
||||
# Remove the KC prefixes
|
||||
macro_keycodes = list(map(lambda s: s.replace('KC_', ''), macro_keycodes))
|
||||
|
||||
macro_data.append({"action": macro_action, "keycodes": macro_keycodes})
|
||||
else:
|
||||
# Found text
|
||||
macro_data.append(m)
|
||||
@ -54,13 +70,13 @@ def _convert_macros(via_macros):
|
||||
|
||||
|
||||
def _fix_macro_keys(keymap_data):
|
||||
macro_no = re.compile(r'MACRO0?([0-9]{1,2})')
|
||||
macro_no = re.compile(r'MACRO0?\(([0-9]{1,2})\)')
|
||||
for i in range(0, len(keymap_data)):
|
||||
for j in range(0, len(keymap_data[i])):
|
||||
kc = keymap_data[i][j]
|
||||
m = macro_no.match(kc)
|
||||
if m:
|
||||
keymap_data[i][j] = f'MACRO_{m.group(1)}'
|
||||
keymap_data[i][j] = f'MC_{m.group(1)}'
|
||||
return keymap_data
|
||||
|
||||
|
||||
|
@ -22,7 +22,7 @@ QMK_FIRMWARE_UPSTREAM = 'qmk/qmk_firmware'
|
||||
MAX_KEYBOARD_SUBFOLDERS = 5
|
||||
|
||||
# Supported processor types
|
||||
CHIBIOS_PROCESSORS = 'cortex-m0', 'cortex-m0plus', 'cortex-m3', 'cortex-m4', 'MKL26Z64', 'MK20DX128', 'MK20DX256', 'MK64FX512', 'MK66FX1M0', 'RP2040', 'STM32F042', 'STM32F072', 'STM32F103', 'STM32F303', 'STM32F401', 'STM32F405', 'STM32F407', 'STM32F411', 'STM32F446', 'STM32G431', 'STM32G474', 'STM32H723', 'STM32H733', 'STM32L412', 'STM32L422', 'STM32L432', 'STM32L433', 'STM32L442', 'STM32L443', 'GD32VF103', 'WB32F3G71', 'WB32FQ95'
|
||||
CHIBIOS_PROCESSORS = 'cortex-m0', 'cortex-m0plus', 'cortex-m3', 'cortex-m4', 'MKL26Z64', 'MK20DX128', 'MK20DX256', 'MK64FX512', 'MK66FX1M0', 'RP2040', 'STM32F042', 'STM32F072', 'STM32F103', 'STM32F303', 'STM32F401', 'STM32F405', 'STM32F407', 'STM32F411', 'STM32F446', 'STM32G431', 'STM32G474', 'STM32H723', 'STM32H733', 'STM32L412', 'STM32L422', 'STM32L432', 'STM32L433', 'STM32L442', 'STM32L443', 'GD32VF103', 'WB32F3G71', 'WB32FQ95', 'AT32F415'
|
||||
LUFA_PROCESSORS = 'at90usb162', 'atmega16u2', 'atmega32u2', 'atmega16u4', 'atmega32u4', 'at90usb646', 'at90usb647', 'at90usb1286', 'at90usb1287', None
|
||||
VUSB_PROCESSORS = 'atmega32a', 'atmega328p', 'atmega328', 'attiny85'
|
||||
|
||||
@ -55,6 +55,7 @@ MCU2BOOTLOADER = {
|
||||
"GD32VF103": "gd32v-dfu",
|
||||
"WB32F3G71": "wb32-dfu",
|
||||
"WB32FQ95": "wb32-dfu",
|
||||
"AT32F415": "at32-dfu",
|
||||
"atmega16u2": "atmel-dfu",
|
||||
"atmega32u2": "atmel-dfu",
|
||||
"atmega16u4": "atmel-dfu",
|
||||
@ -93,6 +94,7 @@ BOOTLOADER_VIDS_PIDS = {
|
||||
'apm32-dfu': {("314b", "0106")},
|
||||
'gd32v-dfu': {("28e9", "0189")},
|
||||
'wb32-dfu': {("342d", "dfa0")},
|
||||
'at32-dfu': {("2e3c", "df11")},
|
||||
'bootloadhid': {("16c0", "05df")},
|
||||
'usbasploader': {("16c0", "05dc")},
|
||||
'usbtinyisp': {("1782", "0c9f")},
|
||||
|
@ -3,6 +3,7 @@
|
||||
import datetime
|
||||
import math
|
||||
import re
|
||||
from pathlib import Path
|
||||
from string import Template
|
||||
from PIL import Image, ImageOps
|
||||
|
||||
@ -137,10 +138,31 @@ def _render_image_metadata(metadata):
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def generate_subs(cli, out_bytes, *, font_metadata=None, image_metadata=None, command):
|
||||
def command_args_str(cli, command_name):
|
||||
"""Given a command name, introspect milc to get the arguments passed in."""
|
||||
|
||||
args = {}
|
||||
max_length = 0
|
||||
for arg_name, was_passed in cli.args_passed[command_name].items():
|
||||
max_length = max(max_length, len(arg_name))
|
||||
|
||||
val = getattr(cli.args, arg_name.replace("-", "_"))
|
||||
|
||||
# do not leak full paths, keep just file name
|
||||
if isinstance(val, Path):
|
||||
val = val.name
|
||||
|
||||
args[arg_name] = val
|
||||
|
||||
return "\n".join(f"// {arg_name.ljust(max_length)} | {val}" for arg_name, val in args.items())
|
||||
|
||||
|
||||
def generate_subs(cli, out_bytes, *, font_metadata=None, image_metadata=None, command_name):
|
||||
if font_metadata is not None and image_metadata is not None:
|
||||
raise ValueError("Cant generate subs for font and image at the same time")
|
||||
|
||||
args = command_args_str(cli, command_name)
|
||||
|
||||
subs = {
|
||||
"year": datetime.date.today().strftime("%Y"),
|
||||
"input_file": cli.args.input.name,
|
||||
@ -148,7 +170,8 @@ def generate_subs(cli, out_bytes, *, font_metadata=None, image_metadata=None, co
|
||||
"byte_count": len(out_bytes),
|
||||
"bytes_lines": render_bytes(out_bytes),
|
||||
"format": cli.args.format,
|
||||
"generator_command": command,
|
||||
"generator_command": command_name.replace("_", "-"),
|
||||
"command_args": args,
|
||||
}
|
||||
|
||||
if font_metadata is not None:
|
||||
@ -167,7 +190,7 @@ def generate_subs(cli, out_bytes, *, font_metadata=None, image_metadata=None, co
|
||||
subs.update({
|
||||
"generated_type": "image",
|
||||
"var_prefix": "gfx",
|
||||
"generator_command": command,
|
||||
"generator_command": command_name,
|
||||
"metadata": _render_image_metadata(image_metadata),
|
||||
})
|
||||
|
||||
@ -183,7 +206,8 @@ license_template = """\
|
||||
// Copyright ${year} QMK -- generated source code only, ${generated_type} retains original copyright
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// This file was auto-generated by `${generator_command}`
|
||||
// This file was auto-generated by `${generator_command}` with arguments:
|
||||
${command_args}
|
||||
"""
|
||||
|
||||
|
||||
|
101
platforms/chibios/boards/GENERIC_AT32_F415XX/board/board.c
Normal file
101
platforms/chibios/boards/GENERIC_AT32_F415XX/board/board.c
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
|
||||
ChibiOS - Copyright (C) 2023..2024 HorrorTroll
|
||||
ChibiOS - Copyright (C) 2023..2024 Zhaqian
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "hal.h"
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief PAL setup.
|
||||
* @details Digital I/O ports static configuration as defined in @p board.h.
|
||||
* This variable is used by the HAL when initializing the PAL driver.
|
||||
*/
|
||||
#if HAL_USE_PAL || defined(__DOXYGEN__)
|
||||
const PALConfig pal_default_config =
|
||||
{
|
||||
{VAL_GPIOAODT, VAL_GPIOACFGLR, VAL_GPIOACFGHR},
|
||||
{VAL_GPIOBODT, VAL_GPIOBCFGLR, VAL_GPIOBCFGHR},
|
||||
#if AT32_HAS_GPIOC
|
||||
{VAL_GPIOCODT, VAL_GPIOCCFGLR, VAL_GPIOCCFGHR},
|
||||
#endif
|
||||
{VAL_GPIODODT, VAL_GPIODCFGLR, VAL_GPIODCFGHR},
|
||||
#if AT32_HAS_GPIOF
|
||||
{VAL_GPIOFODT, VAL_GPIOFCFGLR, VAL_GPIOFCFGHR},
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Early initialization code.
|
||||
* @details System clocks are initialized before everything else.
|
||||
*/
|
||||
void __early_init(void) {
|
||||
at32_clock_init();
|
||||
}
|
||||
|
||||
#if HAL_USE_SDC || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief SDC card detection.
|
||||
*/
|
||||
bool sdc_lld_is_card_inserted(SDCDriver *sdcp) {
|
||||
static bool last_status = false;
|
||||
|
||||
if (blkIsTransferring(sdcp))
|
||||
return last_status;
|
||||
return last_status = (bool)palReadPad(GPIOC, GPIOC_PIN11);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SDC card write protection detection.
|
||||
*/
|
||||
bool sdc_lld_is_write_protected(SDCDriver *sdcp) {
|
||||
|
||||
(void)sdcp;
|
||||
return false;
|
||||
}
|
||||
#endif /* HAL_USE_SDC */
|
||||
|
||||
/**
|
||||
* @brief Board-specific initialization code.
|
||||
* @note You can add your board-specific code here.
|
||||
*/
|
||||
void boardInit(void) {
|
||||
IOMUX->REMAP |= IOMUX_REMAP_SWJTAG_MUX_JTAGDIS;
|
||||
}
|
207
platforms/chibios/boards/GENERIC_AT32_F415XX/board/board.h
Normal file
207
platforms/chibios/boards/GENERIC_AT32_F415XX/board/board.h
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
|
||||
ChibiOS - Copyright (C) 2023..2024 HorrorTroll
|
||||
ChibiOS - Copyright (C) 2023..2024 Zhaqian
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _BOARD_H_
|
||||
#define _BOARD_H_
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*
|
||||
* Setup for a Generic AT32F415 board.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Board identifier.
|
||||
*/
|
||||
#define BOARD_GENERIC_AT32_F415XX
|
||||
#define BOARD_NAME "GENERIC AT32F415 board"
|
||||
|
||||
/*
|
||||
* Board oscillators-related settings.
|
||||
*/
|
||||
#if !defined(AT32_LEXTCLK)
|
||||
#define AT32_LEXTCLK 32768
|
||||
#endif
|
||||
|
||||
#if !defined(AT32_HEXTCLK)
|
||||
#define AT32_HEXTCLK 8000000
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MCU type, supported types are defined in ./os/hal/platforms/hal_lld.h.
|
||||
*/
|
||||
#define AT32F415KB
|
||||
|
||||
/*
|
||||
* IO pins assignments.
|
||||
*/
|
||||
#define GPIOA_PIN0 0U
|
||||
#define GPIOA_PIN1 1U
|
||||
#define GPIOA_PIN2 2U
|
||||
#define GPIOA_PIN3 3U
|
||||
#define GPIOA_PIN4 4U
|
||||
#define GPIOA_PIN5 5U
|
||||
#define GPIOA_PIN6 6U
|
||||
#define GPIOA_PIN7 7U
|
||||
#define GPIOA_PIN8 8U
|
||||
#define GPIOA_PIN9 9U
|
||||
#define GPIOA_PIN10 10U
|
||||
#define GPIOA_PIN11 11U
|
||||
#define GPIOA_PIN12 12U
|
||||
#define GPIOA_SWDIO 13U
|
||||
#define GPIOA_SWCLK 14U
|
||||
#define GPIOA_PIN15 15U
|
||||
|
||||
#define GPIOB_PIN0 0U
|
||||
#define GPIOB_PIN1 1U
|
||||
#define GPIOB_PIN2 2U
|
||||
#define GPIOB_PIN3 3U
|
||||
#define GPIOB_PIN4 4U
|
||||
#define GPIOB_PIN5 5U
|
||||
#define GPIOB_PIN6 6U
|
||||
#define GPIOB_PIN7 7U
|
||||
#define GPIOB_PIN8 8U
|
||||
#define GPIOB_PIN9 9U
|
||||
#define GPIOB_PIN10 10U
|
||||
#define GPIOB_PIN11 11U
|
||||
#define GPIOB_PIN12 12U
|
||||
#define GPIOB_PIN13 13U
|
||||
#define GPIOB_PIN14 14U
|
||||
#define GPIOB_PIN15 15U
|
||||
|
||||
#define GPIOC_PIN0 0U
|
||||
#define GPIOC_PIN1 1U
|
||||
#define GPIOC_PIN2 2U
|
||||
#define GPIOC_PIN3 3U
|
||||
#define GPIOC_PIN4 4U
|
||||
#define GPIOC_PIN5 5U
|
||||
#define GPIOC_PIN6 6U
|
||||
#define GPIOC_PIN7 7U
|
||||
#define GPIOC_PIN8 8U
|
||||
#define GPIOC_PIN9 9U
|
||||
#define GPIOC_PIN10 10U
|
||||
#define GPIOC_PIN11 11U
|
||||
#define GPIOC_PIN12 12U
|
||||
#define GPIOC_PIN13 13U
|
||||
#define GPIOC_PIN14 14U
|
||||
#define GPIOC_PIN15 15U
|
||||
|
||||
#define GPIOD_HEXT_IN 0U
|
||||
#define GPIOD_HEXT_OUT 1U
|
||||
#define GPIOD_PIN2 2U
|
||||
|
||||
#define GPIOF_PIN4 4U
|
||||
#define GPIOF_PIN5 5U
|
||||
#define GPIOF_PIN6 6U
|
||||
#define GPIOF_PIN7 7U
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*
|
||||
* I/O ports initial setup, this configuration is established soon after reset
|
||||
* in the initialization code.
|
||||
*
|
||||
* The digits have the following meaning:
|
||||
* 0 - Analog input.
|
||||
* 1 - Push Pull output 10MHz.
|
||||
* 2 - Push Pull output 2MHz.
|
||||
* 3 - Push Pull output 50MHz.
|
||||
* 4 - Digital input.
|
||||
* 5 - Open Drain output 10MHz.
|
||||
* 6 - Open Drain output 2MHz.
|
||||
* 7 - Open Drain output 50MHz.
|
||||
* 8 - Digital input with Pull-Up or Pull-Down resistor depending on ODT.
|
||||
* 9 - Alternate Push Pull output 10MHz.
|
||||
* A - Alternate Push Pull output 2MHz.
|
||||
* B - Alternate Push Pull output 50MHz.
|
||||
* C - Reserved.
|
||||
* D - Alternate Open Drain output 10MHz.
|
||||
* E - Alternate Open Drain output 2MHz.
|
||||
* F - Alternate Open Drain output 50MHz.
|
||||
* Please refer to the AT32 Reference Manual for details.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Port A setup.
|
||||
*/
|
||||
#define VAL_GPIOACFGLR 0x88888B88 /* PA7...PA0 */
|
||||
#define VAL_GPIOACFGHR 0x888888B8 /* PA15...PA8 */
|
||||
#define VAL_GPIOAODT 0xFFFFFFFF
|
||||
|
||||
/*
|
||||
* Port B setup.
|
||||
*/
|
||||
#define VAL_GPIOBCFGLR 0x88888888 /* PB7...PB0 */
|
||||
#define VAL_GPIOBCFGHR 0x88888888 /* PB15...PB8 */
|
||||
#define VAL_GPIOBODT 0xFFFFFFFF
|
||||
|
||||
/*
|
||||
* Port C setup.
|
||||
*/
|
||||
#define VAL_GPIOCCFGLR 0x88888888 /* PC7...PC0 */
|
||||
#define VAL_GPIOCCFGHR 0x88888888 /* PC15...PC8 */
|
||||
#define VAL_GPIOCODT 0xFFFFFFFF
|
||||
|
||||
/*
|
||||
* Port D setup.
|
||||
* Everything input with pull-up except:
|
||||
* PD0 - Normal input (GPIOD_HEXT_IN).
|
||||
* PD1 - Normal input (GPIOD_HEXT_OUT).
|
||||
*/
|
||||
#define VAL_GPIODCFGLR 0x88888844 /* PD7...PD0 */
|
||||
#define VAL_GPIODCFGHR 0x88888888 /* PD15...PD8 */
|
||||
#define VAL_GPIODODT 0xFFFFFFFF
|
||||
|
||||
/*
|
||||
* Port F setup.
|
||||
*/
|
||||
#define VAL_GPIOFCFGLR 0x88888888 /* PF7...PF0 */
|
||||
#define VAL_GPIOFCFGHR 0x88888888 /* PF15...PF8 */
|
||||
#define VAL_GPIOFODT 0xFFFFFFFF
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if !defined(_FROM_ASM_)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void boardInit(void);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _FROM_ASM_ */
|
||||
|
||||
#endif /* _BOARD_H_ */
|
@ -0,0 +1,9 @@
|
||||
# List of all the board related files.
|
||||
BOARDSRC = $(BOARD_PATH)/board/board.c
|
||||
|
||||
# Required include directories
|
||||
BOARDINC = $(BOARD_PATH)/board
|
||||
|
||||
# Shared variables
|
||||
ALLCSRC += $(BOARDSRC)
|
||||
ALLINC += $(BOARDINC)
|
@ -0,0 +1,13 @@
|
||||
// Copyright 2023-2024 HorrorTroll <https://github.com/HorrorTroll>
|
||||
// Copyright 2023-2024 Zhaqian <https://github.com/zhaqian12>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#define BOARD_OTG_VBUSIG
|
||||
|
||||
#define USB_ENDPOINTS_ARE_REORDERABLE
|
||||
|
||||
#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP
|
||||
# define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE
|
||||
#endif
|
236
platforms/chibios/boards/GENERIC_AT32_F415XX/configs/mcuconf.h
Normal file
236
platforms/chibios/boards/GENERIC_AT32_F415XX/configs/mcuconf.h
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio
|
||||
ChibiOS - Copyright (C) 2023..2024 HorrorTroll
|
||||
ChibiOS - Copyright (C) 2023..2024 Zhaqian
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef MCUCONF_H
|
||||
#define MCUCONF_H
|
||||
|
||||
/*
|
||||
* AT32F415 drivers configuration.
|
||||
* The following settings override the default settings present in
|
||||
* the various device driver implementation headers.
|
||||
* Note that the settings for each driver only have effect if the whole
|
||||
* driver is enabled in halconf.h.
|
||||
*
|
||||
* IRQ priorities:
|
||||
* 15...0 Lowest...Highest.
|
||||
*
|
||||
* DMA priorities:
|
||||
* 0...3 Lowest...Highest.
|
||||
*/
|
||||
|
||||
#define AT32F415_MCUCONF
|
||||
|
||||
/*
|
||||
* General settings.
|
||||
*/
|
||||
#define AT32_NO_INIT FALSE
|
||||
|
||||
/*
|
||||
* HAL driver system settings.
|
||||
*/
|
||||
#define AT32_HICK_ENABLED TRUE
|
||||
#define AT32_LICK_ENABLED FALSE
|
||||
#define AT32_HEXT_ENABLED TRUE
|
||||
#define AT32_LEXT_ENABLED FALSE
|
||||
#define AT32_SCLKSEL AT32_SCLKSEL_PLL
|
||||
#define AT32_PLLRCS AT32_PLLRCS_HEXT
|
||||
#define AT32_PLLHEXTDIV AT32_PLLHEXTDIV_DIV1
|
||||
#define AT32_PLLCFGEN AT32_PLLCFGEN_SOLID
|
||||
#define AT32_PLLMULT_VALUE 18
|
||||
#define AT32_PLL_FR_VALUE 4
|
||||
#define AT32_PLL_MS_VALUE 1
|
||||
#define AT32_PLL_NS_VALUE 72
|
||||
#define AT32_AHBDIV AT32_AHBDIV_DIV1
|
||||
#define AT32_APB1DIV AT32_APB1DIV_DIV2
|
||||
#define AT32_APB2DIV AT32_APB2DIV_DIV2
|
||||
#define AT32_ADCDIV AT32_ADCDIV_DIV4
|
||||
#define AT32_USB_CLOCK_REQUIRED TRUE
|
||||
#define AT32_USBDIV AT32_USBDIV_DIV3
|
||||
#define AT32_CLKOUT_SEL AT32_CLKOUT_SEL_NOCLOCK
|
||||
#define AT32_CLKOUTDIV AT32_CLKOUTDIV_DIV1
|
||||
#define AT32_ERTCSEL AT32_ERTCSEL_HEXTDIV
|
||||
#define AT32_PVM_ENABLE FALSE
|
||||
#define AT32_PVMSEL AT32_PVMSEL_LEV1
|
||||
|
||||
/*
|
||||
* IRQ system settings.
|
||||
*/
|
||||
#define AT32_IRQ_EXINT0_PRIORITY 6
|
||||
#define AT32_IRQ_EXINT1_PRIORITY 6
|
||||
#define AT32_IRQ_EXINT2_PRIORITY 6
|
||||
#define AT32_IRQ_EXINT3_PRIORITY 6
|
||||
#define AT32_IRQ_EXINT4_PRIORITY 6
|
||||
#define AT32_IRQ_EXINT5_9_PRIORITY 6
|
||||
#define AT32_IRQ_EXINT10_15_PRIORITY 6
|
||||
#define AT32_IRQ_EXINT16_PRIORITY 6
|
||||
#define AT32_IRQ_EXINT17_PRIORITY 15
|
||||
#define AT32_IRQ_EXINT18_PRIORITY 6
|
||||
#define AT32_IRQ_EXINT19_PRIORITY 6
|
||||
#define AT32_IRQ_EXINT20_PRIORITY 6
|
||||
#define AT32_IRQ_EXINT21_PRIORITY 15
|
||||
#define AT32_IRQ_EXINT22_PRIORITY 15
|
||||
|
||||
#define AT32_IRQ_TMR1_BRK_TMR9_PRIORITY 7
|
||||
#define AT32_IRQ_TMR1_OVF_TMR10_PRIORITY 7
|
||||
#define AT32_IRQ_TMR1_HALL_TMR11_PRIORITY 7
|
||||
#define AT32_IRQ_TMR1_CH_PRIORITY 7
|
||||
#define AT32_IRQ_TMR2_PRIORITY 7
|
||||
#define AT32_IRQ_TMR3_PRIORITY 7
|
||||
#define AT32_IRQ_TMR4_PRIORITY 7
|
||||
#define AT32_IRQ_TMR5_PRIORITY 7
|
||||
|
||||
#define AT32_IRQ_USART1_PRIORITY 12
|
||||
#define AT32_IRQ_USART2_PRIORITY 12
|
||||
#define AT32_IRQ_USART3_PRIORITY 12
|
||||
#define AT32_IRQ_UART4_PRIORITY 12
|
||||
#define AT32_IRQ_UART5_PRIORITY 12
|
||||
|
||||
/*
|
||||
* ADC driver system settings.
|
||||
*/
|
||||
#define AT32_ADC_USE_ADC1 FALSE
|
||||
#define AT32_ADC_ADC1_DMA_PRIORITY 2
|
||||
#define AT32_ADC_ADC1_IRQ_PRIORITY 6
|
||||
|
||||
/*
|
||||
* CAN driver system settings.
|
||||
*/
|
||||
#define AT32_CAN_USE_CAN1 FALSE
|
||||
#define AT32_CAN_CAN1_IRQ_PRIORITY 11
|
||||
|
||||
/*
|
||||
* DMA driver system settings.
|
||||
*/
|
||||
#define AT32_DMA_USE_DMAMUX TRUE
|
||||
|
||||
/*
|
||||
* GPT driver system settings.
|
||||
*/
|
||||
#define AT32_GPT_USE_TMR1 FALSE
|
||||
#define AT32_GPT_USE_TMR2 FALSE
|
||||
#define AT32_GPT_USE_TMR3 FALSE
|
||||
#define AT32_GPT_USE_TMR4 FALSE
|
||||
#define AT32_GPT_USE_TMR5 FALSE
|
||||
#define AT32_GPT_USE_TMR9 FALSE
|
||||
#define AT32_GPT_USE_TMR10 FALSE
|
||||
#define AT32_GPT_USE_TMR11 FALSE
|
||||
|
||||
/*
|
||||
* I2C driver system settings.
|
||||
*/
|
||||
#define AT32_I2C_USE_I2C1 FALSE
|
||||
#define AT32_I2C_USE_I2C2 FALSE
|
||||
#define AT32_I2C_BUSY_TIMEOUT 50
|
||||
#define AT32_I2C_I2C1_IRQ_PRIORITY 5
|
||||
#define AT32_I2C_I2C2_IRQ_PRIORITY 5
|
||||
#define AT32_I2C_I2C1_DMA_PRIORITY 3
|
||||
#define AT32_I2C_I2C2_DMA_PRIORITY 3
|
||||
#define AT32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure")
|
||||
|
||||
/*
|
||||
* ICU driver system settings.
|
||||
*/
|
||||
#define AT32_ICU_USE_TMR1 FALSE
|
||||
#define AT32_ICU_USE_TMR2 FALSE
|
||||
#define AT32_ICU_USE_TMR3 FALSE
|
||||
#define AT32_ICU_USE_TMR4 FALSE
|
||||
#define AT32_ICU_USE_TMR5 FALSE
|
||||
#define AT32_ICU_USE_TMR9 FALSE
|
||||
#define AT32_ICU_USE_TMR10 FALSE
|
||||
#define AT32_ICU_USE_TMR11 FALSE
|
||||
|
||||
/*
|
||||
* PWM driver system settings.
|
||||
*/
|
||||
#define AT32_PWM_USE_TMR1 FALSE
|
||||
#define AT32_PWM_USE_TMR2 FALSE
|
||||
#define AT32_PWM_USE_TMR3 FALSE
|
||||
#define AT32_PWM_USE_TMR4 FALSE
|
||||
#define AT32_PWM_USE_TMR5 FALSE
|
||||
#define AT32_PWM_USE_TMR9 FALSE
|
||||
#define AT32_PWM_USE_TMR10 FALSE
|
||||
#define AT32_PWM_USE_TMR11 FALSE
|
||||
|
||||
/*
|
||||
* RTC driver system settings.
|
||||
*/
|
||||
#define AT32_ERTC_DIVA_VALUE 32
|
||||
#define AT32_ERTC_DIVB_VALUE 1024
|
||||
#define AT32_ERTC_CTRL_INIT 0
|
||||
#define AT32_ERTC_TAMP_INIT 0
|
||||
|
||||
/*
|
||||
* SDC driver system settings.
|
||||
*/
|
||||
#define AT32_SDC_SDIO_DMA_PRIORITY 3
|
||||
#define AT32_SDC_SDIO_IRQ_PRIORITY 9
|
||||
#define AT32_SDC_WRITE_TIMEOUT_MS 1000
|
||||
#define AT32_SDC_READ_TIMEOUT_MS 1000
|
||||
#define AT32_SDC_CLOCK_ACTIVATION_DELAY 10
|
||||
#define AT32_SDC_SDIO_UNALIGNED_SUPPORT TRUE
|
||||
|
||||
/*
|
||||
* SERIAL driver system settings.
|
||||
*/
|
||||
#define AT32_SERIAL_USE_USART1 FALSE
|
||||
#define AT32_SERIAL_USE_USART2 FALSE
|
||||
#define AT32_SERIAL_USE_USART3 FALSE
|
||||
#define AT32_SERIAL_USE_UART4 FALSE
|
||||
#define AT32_SERIAL_USE_UART5 FALSE
|
||||
|
||||
/*
|
||||
* SPI driver system settings.
|
||||
*/
|
||||
#define AT32_SPI_USE_SPI1 FALSE
|
||||
#define AT32_SPI_USE_SPI2 FALSE
|
||||
#define AT32_SPI_SPI1_DMA_PRIORITY 1
|
||||
#define AT32_SPI_SPI2_DMA_PRIORITY 1
|
||||
#define AT32_SPI_SPI1_IRQ_PRIORITY 10
|
||||
#define AT32_SPI_SPI2_IRQ_PRIORITY 10
|
||||
#define AT32_SPI_DMA_ERROR_HOOK(spip) osalSysHalt("DMA failure")
|
||||
|
||||
/*
|
||||
* ST driver system settings.
|
||||
*/
|
||||
#define AT32_ST_IRQ_PRIORITY 8
|
||||
#define AT32_ST_USE_TIMER 2
|
||||
|
||||
/*
|
||||
* UART driver system settings.
|
||||
*/
|
||||
#define AT32_UART_USE_USART1 FALSE
|
||||
#define AT32_UART_USE_USART2 FALSE
|
||||
#define AT32_UART_USE_USART3 FALSE
|
||||
#define AT32_UART_USART1_DMA_PRIORITY 0
|
||||
#define AT32_UART_USART2_DMA_PRIORITY 0
|
||||
#define AT32_UART_USART3_DMA_PRIORITY 0
|
||||
#define AT32_UART_DMA_ERROR_HOOK(uartp) osalSysHalt("DMA failure")
|
||||
|
||||
/*
|
||||
* USB driver system settings.
|
||||
*/
|
||||
#define AT32_USB_USE_OTG1 TRUE
|
||||
#define AT32_USB_OTG1_IRQ_PRIORITY 14
|
||||
#define AT32_USB_OTG1_RX_FIFO_SIZE 512
|
||||
|
||||
/*
|
||||
* WDG driver system settings.
|
||||
*/
|
||||
#define AT32_WDG_USE_WDT FALSE
|
||||
|
||||
#endif /* MCUCONF_H */
|
@ -26,6 +26,7 @@
|
||||
# stm32-dfu STM32 USB DFU in ROM
|
||||
# apm32-dfu APM32 USB DFU in ROM
|
||||
# wb32-dfu WB32 USB DFU in ROM
|
||||
# at32-dfu AT32 USB DFU in ROM
|
||||
# tinyuf2 TinyUF2
|
||||
# rp2040 Raspberry Pi RP2040
|
||||
# Current options for RISC-V:
|
||||
@ -119,6 +120,14 @@ ifeq ($(strip $(BOOTLOADER)), wb32-dfu)
|
||||
OPT_DEFS += -DBOOTLOADER_WB32_DFU
|
||||
BOOTLOADER_TYPE = wb32_dfu
|
||||
endif
|
||||
ifeq ($(strip $(BOOTLOADER)), at32-dfu)
|
||||
OPT_DEFS += -DBOOTLOADER_AT32_DFU
|
||||
BOOTLOADER_TYPE = at32_dfu
|
||||
|
||||
# Options to pass to dfu-util when flashing
|
||||
DFU_ARGS ?= -d 2E3C:DF11 -a 0 -s 0x08000000:leave
|
||||
DFU_SUFFIX_ARGS ?= -v 2E3C -p DF11
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(BOOTLOADER_TYPE)),)
|
||||
ifneq ($(strip $(BOOTLOADER)),)
|
||||
|
83
platforms/chibios/bootloaders/at32_dfu.c
Normal file
83
platforms/chibios/bootloaders/at32_dfu.c
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright 2021-2023 QMK
|
||||
// Copyright 2023-2024 HorrorTroll <https://github.com/HorrorTroll>
|
||||
// Copyright 2023-2024 Zhaqian <https://github.com/zhaqian12>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "bootloader.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <ch.h>
|
||||
#include <hal.h>
|
||||
#include "wait.h"
|
||||
|
||||
#ifndef AT32_BOOTLOADER_RAM_SYMBOL
|
||||
# define AT32_BOOTLOADER_RAM_SYMBOL __ram0_end__
|
||||
#endif
|
||||
|
||||
extern uint32_t AT32_BOOTLOADER_RAM_SYMBOL;
|
||||
|
||||
/* This code should be checked whether it runs correctly on platforms */
|
||||
#define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0))
|
||||
#define BOOTLOADER_MAGIC 0xDEADBEEF
|
||||
#define MAGIC_ADDR (unsigned long *)(SYMVAL(AT32_BOOTLOADER_RAM_SYMBOL) - 4)
|
||||
|
||||
__attribute__((weak)) void bootloader_marker_enable(void) {
|
||||
uint32_t *marker = (uint32_t *)MAGIC_ADDR;
|
||||
*marker = BOOTLOADER_MAGIC; // set magic flag => reset handler will jump into boot loader
|
||||
}
|
||||
|
||||
__attribute__((weak)) bool bootloader_marker_active(void) {
|
||||
const uint32_t *marker = (const uint32_t *)MAGIC_ADDR;
|
||||
return (*marker == BOOTLOADER_MAGIC) ? true : false;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void bootloader_marker_disable(void) {
|
||||
uint32_t *marker = (uint32_t *)MAGIC_ADDR;
|
||||
*marker = 0;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void bootloader_jump(void) {
|
||||
bootloader_marker_enable();
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
__attribute__((weak)) void mcu_reset(void) {
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
void enter_bootloader_mode_if_requested(void) {
|
||||
if (bootloader_marker_active()) {
|
||||
bootloader_marker_disable();
|
||||
|
||||
struct system_memory_vector_t {
|
||||
uint32_t stack_top;
|
||||
void (*entrypoint)(void);
|
||||
};
|
||||
const struct system_memory_vector_t *bootloader = (const struct system_memory_vector_t *)(AT32_BOOTLOADER_ADDRESS);
|
||||
|
||||
__disable_irq();
|
||||
|
||||
#if defined(__MPU_PRESENT) && (__MPU_PRESENT == 1U)
|
||||
ARM_MPU_Disable();
|
||||
#endif
|
||||
|
||||
SysTick->CTRL = 0;
|
||||
SysTick->VAL = 0;
|
||||
SysTick->LOAD = 0;
|
||||
|
||||
// Clear interrupt enable and interrupt pending registers
|
||||
for (int i = 0; i < ARRAY_SIZE(NVIC->ICER); i++) {
|
||||
NVIC->ICER[i] = 0xFFFFFFFF;
|
||||
NVIC->ICPR[i] = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
__set_CONTROL(0);
|
||||
__set_MSP(bootloader->stack_top);
|
||||
__enable_irq();
|
||||
|
||||
// Jump to bootloader
|
||||
bootloader->entrypoint();
|
||||
while (true) {
|
||||
}
|
||||
}
|
||||
}
|
@ -142,6 +142,19 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// AT32 compatibility
|
||||
#if defined(MCU_AT32)
|
||||
# define CPU_CLOCK AT32_SYSCLK
|
||||
|
||||
# if defined(AT32F415)
|
||||
# define USE_GPIOV1
|
||||
# define USE_I2CV1
|
||||
# define PAL_MODE_ALTERNATE_OPENDRAIN PAL_MODE_AT32_ALTERNATE_OPENDRAIN
|
||||
# define PAL_MODE_ALTERNATE_PUSHPULL PAL_MODE_AT32_ALTERNATE_PUSHPULL
|
||||
# define AUDIO_PWM_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(GD32VF103)
|
||||
/* This chip has the same API as STM32F103, but uses different names for literally the same thing.
|
||||
* As of 4.7.2021 QMK is tailored to use STM32 defines/names, for compatibility sake
|
||||
|
@ -22,7 +22,7 @@
|
||||
# error "You need to set HAL_USE_ADC to TRUE in your halconf.h to use the ADC."
|
||||
#endif
|
||||
|
||||
#if !RP_ADC_USE_ADC1 && !STM32_ADC_USE_ADC1 && !STM32_ADC_USE_ADC2 && !STM32_ADC_USE_ADC3 && !STM32_ADC_USE_ADC4 && !WB32_ADC_USE_ADC1
|
||||
#if !RP_ADC_USE_ADC1 && !STM32_ADC_USE_ADC1 && !STM32_ADC_USE_ADC2 && !STM32_ADC_USE_ADC3 && !STM32_ADC_USE_ADC4 && !WB32_ADC_USE_ADC1 && !AT32_ADC_USE_ADC1
|
||||
# error "You need to set one of the 'xxx_ADC_USE_ADCx' settings to TRUE in your mcuconf.h to use the ADC."
|
||||
#endif
|
||||
|
||||
@ -45,7 +45,7 @@
|
||||
// Otherwise assume V3
|
||||
#if defined(STM32F0XX) || defined(STM32L0XX)
|
||||
# define USE_ADCV1
|
||||
#elif defined(STM32F1XX) || defined(STM32F2XX) || defined(STM32F4XX) || defined(GD32VF103) || defined(WB32F3G71xx) || defined(WB32FQ95xx)
|
||||
#elif defined(STM32F1XX) || defined(STM32F2XX) || defined(STM32F4XX) || defined(GD32VF103) || defined(WB32F3G71xx) || defined(WB32FQ95xx) || defined(AT32F415)
|
||||
# define USE_ADCV2
|
||||
#endif
|
||||
|
||||
@ -82,7 +82,7 @@
|
||||
|
||||
/* User configurable ADC options */
|
||||
#ifndef ADC_COUNT
|
||||
# if defined(RP2040) || defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F4XX) || defined(GD32VF103) || defined(WB32F3G71xx) || defined(WB32FQ95xx)
|
||||
# if defined(RP2040) || defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F4XX) || defined(GD32VF103) || defined(WB32F3G71xx) || defined(WB32FQ95xx) || defined(AT32F415)
|
||||
# define ADC_COUNT 1
|
||||
# elif defined(STM32F3XX) || defined(STM32G4XX)
|
||||
# define ADC_COUNT 4
|
||||
@ -142,11 +142,16 @@ static ADCConversionGroup adcConversionGroup = {
|
||||
.cfgr1 = ADC_CFGR1_CONT | ADC_RESOLUTION,
|
||||
.smpr = ADC_SAMPLING_RATE,
|
||||
#elif defined(USE_ADCV2)
|
||||
# if !defined(STM32F1XX) && !defined(GD32VF103) && !defined(WB32F3G71xx) && !defined(WB32FQ95xx)
|
||||
.cr2 = ADC_CR2_SWSTART, // F103 seem very unhappy with, F401 seems very unhappy without...
|
||||
# if !defined(STM32F1XX) && !defined(GD32VF103) && !defined(WB32F3G71xx) && !defined(WB32FQ95xx) && !defined(AT32F415)
|
||||
.cr2 = ADC_CR2_SWSTART, // F103 seem very unhappy with, F401 seems very unhappy without...
|
||||
# endif
|
||||
# if defined(AT32F415)
|
||||
.spt2 = ADC_SPT2_CSPT_AN0(ADC_SAMPLING_RATE) | ADC_SPT2_CSPT_AN1(ADC_SAMPLING_RATE) | ADC_SPT2_CSPT_AN2(ADC_SAMPLING_RATE) | ADC_SPT2_CSPT_AN3(ADC_SAMPLING_RATE) | ADC_SPT2_CSPT_AN4(ADC_SAMPLING_RATE) | ADC_SPT2_CSPT_AN5(ADC_SAMPLING_RATE) | ADC_SPT2_CSPT_AN6(ADC_SAMPLING_RATE) | ADC_SPT2_CSPT_AN7(ADC_SAMPLING_RATE) | ADC_SPT2_CSPT_AN8(ADC_SAMPLING_RATE) | ADC_SPT2_CSPT_AN9(ADC_SAMPLING_RATE),
|
||||
.spt1 = ADC_SPT1_CSPT_AN10(ADC_SAMPLING_RATE) | ADC_SPT1_CSPT_AN11(ADC_SAMPLING_RATE) | ADC_SPT1_CSPT_AN12(ADC_SAMPLING_RATE) | ADC_SPT1_CSPT_AN13(ADC_SAMPLING_RATE) | ADC_SPT1_CSPT_AN14(ADC_SAMPLING_RATE) | ADC_SPT1_CSPT_AN15(ADC_SAMPLING_RATE),
|
||||
# else
|
||||
.smpr2 = ADC_SMPR2_SMP_AN0(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN1(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN2(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN3(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN4(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN5(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN6(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN7(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN8(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN9(ADC_SAMPLING_RATE),
|
||||
.smpr1 = ADC_SMPR1_SMP_AN10(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN11(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN12(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN13(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN14(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN15(ADC_SAMPLING_RATE),
|
||||
# endif
|
||||
#elif defined(RP2040)
|
||||
// RP2040 does not have any extra config here
|
||||
#else
|
||||
@ -242,7 +247,7 @@ __attribute__((weak)) adc_mux pinToMux(pin_t pin) {
|
||||
case F9: return TO_MUX( ADC_CHANNEL_IN7, 2 );
|
||||
case F10: return TO_MUX( ADC_CHANNEL_IN8, 2 );
|
||||
# endif
|
||||
#elif defined(STM32F1XX) || defined(GD32VF103) || defined(WB32F3G71xx) || defined(WB32FQ95xx)
|
||||
#elif defined(STM32F1XX) || defined(GD32VF103) || defined(WB32F3G71xx) || defined(WB32FQ95xx) || defined(AT32F415)
|
||||
case A0: return TO_MUX( ADC_CHANNEL_IN0, 0 );
|
||||
case A1: return TO_MUX( ADC_CHANNEL_IN1, 0 );
|
||||
case A2: return TO_MUX( ADC_CHANNEL_IN2, 0 );
|
||||
@ -344,7 +349,7 @@ __attribute__((weak)) adc_mux pinToMux(pin_t pin) {
|
||||
|
||||
static inline ADCDriver* intToADCDriver(uint8_t adcInt) {
|
||||
switch (adcInt) {
|
||||
#if RP_ADC_USE_ADC1 || STM32_ADC_USE_ADC1 || WB32_ADC_USE_ADC1
|
||||
#if RP_ADC_USE_ADC1 || STM32_ADC_USE_ADC1 || WB32_ADC_USE_ADC1 || AT32_ADC_USE_ADC1
|
||||
case 0:
|
||||
return &ADCD1;
|
||||
#endif
|
||||
@ -391,7 +396,11 @@ int16_t adc_read(adc_mux mux) {
|
||||
// TODO: fix previous assumption of only 1 input...
|
||||
adcConversionGroup.chselr = 1 << mux.input; /*no macro to convert N to ADC_CHSELR_CHSEL1*/
|
||||
#elif defined(USE_ADCV2)
|
||||
# if defined(AT32F415)
|
||||
adcConversionGroup.osq3 = ADC_OSQ3_OSN1_N(mux.input);
|
||||
# else
|
||||
adcConversionGroup.sqr3 = ADC_SQR3_SQ1_N(mux.input);
|
||||
# endif
|
||||
#elif defined(RP2040)
|
||||
adcConversionGroup.channel_mask = 1 << mux.input;
|
||||
#else
|
||||
|
@ -9,6 +9,17 @@
|
||||
|
||||
#if defined(SERIAL_USART_CONFIG)
|
||||
static QMKSerialConfig serial_config = SERIAL_USART_CONFIG;
|
||||
#elif defined(MCU_AT32) /* AT32 MCUs */
|
||||
static QMKSerialConfig serial_config = {
|
||||
.speed = (SERIAL_USART_SPEED),
|
||||
.ctrl1 = (SERIAL_USART_CTRL1),
|
||||
.ctrl2 = (SERIAL_USART_CTRL2),
|
||||
# if !defined(SERIAL_USART_FULL_DUPLEX)
|
||||
.ctrl3 = ((SERIAL_USART_CTRL3) | USART_CTRL3_SLBEN) /* activate half-duplex mode */
|
||||
# else
|
||||
.ctrl3 = (SERIAL_USART_CTRL3)
|
||||
# endif
|
||||
};
|
||||
#elif defined(MCU_STM32) /* STM32 MCUs */
|
||||
static QMKSerialConfig serial_config = {
|
||||
# if HAL_USE_SERIAL
|
||||
@ -160,7 +171,7 @@ inline bool serial_transport_receive_blocking(uint8_t* destination, const size_t
|
||||
* @brief Initiate pins for USART peripheral. Half-duplex configuration.
|
||||
*/
|
||||
__attribute__((weak)) void usart_init(void) {
|
||||
# if defined(MCU_STM32) /* STM32 MCUs */
|
||||
# if defined(MCU_STM32) || defined(MCU_AT32) /* STM32 and AT32 MCUs */
|
||||
# if defined(USE_GPIOV1)
|
||||
palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_OPENDRAIN);
|
||||
# else
|
||||
@ -183,7 +194,7 @@ __attribute__((weak)) void usart_init(void) {
|
||||
* @brief Initiate pins for USART peripheral. Full-duplex configuration.
|
||||
*/
|
||||
__attribute__((weak)) void usart_init(void) {
|
||||
# if defined(MCU_STM32) /* STM32 MCUs */
|
||||
# if defined(MCU_STM32) || defined(MCU_AT32) /* STM32 and AT32 MCUs */
|
||||
# if defined(USE_GPIOV1)
|
||||
palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_PUSHPULL);
|
||||
palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT);
|
||||
|
@ -74,40 +74,75 @@ typedef SIOConfig QMKSerialConfig;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(USART_CR1_M0)
|
||||
# define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so
|
||||
#endif
|
||||
#if defined(MCU_STM32) /* STM32 MCUs */
|
||||
# if !defined(USART_CR1_M0)
|
||||
# define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so
|
||||
# endif
|
||||
|
||||
#if !defined(SERIAL_USART_CR1)
|
||||
# define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0) // parity enable, odd parity, 9 bit length
|
||||
#endif
|
||||
# if !defined(SERIAL_USART_CR1)
|
||||
# define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0) // parity enable, odd parity, 9 bit length
|
||||
# endif
|
||||
|
||||
#if !defined(SERIAL_USART_CR2)
|
||||
# define SERIAL_USART_CR2 (USART_CR2_STOP_1) // 2 stop bits
|
||||
#endif
|
||||
# if !defined(SERIAL_USART_CR2)
|
||||
# define SERIAL_USART_CR2 (USART_CR2_STOP_1) // 2 stop bits
|
||||
# endif
|
||||
|
||||
#if !defined(SERIAL_USART_CR3)
|
||||
# define SERIAL_USART_CR3 0
|
||||
#endif
|
||||
# if !defined(SERIAL_USART_CR3)
|
||||
# define SERIAL_USART_CR3 0
|
||||
# endif
|
||||
|
||||
#if defined(USART1_REMAP)
|
||||
# define USART_REMAP \
|
||||
do { \
|
||||
(AFIO->MAPR |= AFIO_MAPR_USART1_REMAP); \
|
||||
} while (0)
|
||||
#elif defined(USART2_REMAP)
|
||||
# define USART_REMAP \
|
||||
do { \
|
||||
(AFIO->MAPR |= AFIO_MAPR_USART2_REMAP); \
|
||||
} while (0)
|
||||
#elif defined(USART3_PARTIALREMAP)
|
||||
# define USART_REMAP \
|
||||
do { \
|
||||
(AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_PARTIALREMAP); \
|
||||
} while (0)
|
||||
#elif defined(USART3_FULLREMAP)
|
||||
# define USART_REMAP \
|
||||
do { \
|
||||
(AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_FULLREMAP); \
|
||||
} while (0)
|
||||
# if defined(USART1_REMAP)
|
||||
# define USART_REMAP \
|
||||
do { \
|
||||
(AFIO->MAPR |= AFIO_MAPR_USART1_REMAP); \
|
||||
} while (0)
|
||||
# elif defined(USART2_REMAP)
|
||||
# define USART_REMAP \
|
||||
do { \
|
||||
(AFIO->MAPR |= AFIO_MAPR_USART2_REMAP); \
|
||||
} while (0)
|
||||
# elif defined(USART3_PARTIALREMAP)
|
||||
# define USART_REMAP \
|
||||
do { \
|
||||
(AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_PARTIALREMAP); \
|
||||
} while (0)
|
||||
# elif defined(USART3_FULLREMAP)
|
||||
# define USART_REMAP \
|
||||
do { \
|
||||
(AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_FULLREMAP); \
|
||||
} while (0)
|
||||
# endif
|
||||
#elif defined(MCU_AT32) /* AT32 MCUs */
|
||||
# if !defined(USART_CTRL1_DBN0)
|
||||
# define USART_CTRL1_DBN0 USART_CTRL1_DBN
|
||||
# endif
|
||||
|
||||
# if !defined(SERIAL_USART_CTRL1)
|
||||
# define SERIAL_USART_CTRL1 (USART_CTRL1_PEN | USART_CTRL1_PSEL | USART_CTRL1_DBN0) // parity enable, odd parity, 9 bit length
|
||||
# endif
|
||||
|
||||
# if !defined(SERIAL_USART_CTRL2)
|
||||
# define SERIAL_USART_CTRL2 (USART_CTRL2_STOPBN_1) // 2 stop bits
|
||||
# endif
|
||||
|
||||
# if !defined(SERIAL_USART_CTRL3)
|
||||
# define SERIAL_USART_CTRL3 0
|
||||
# endif
|
||||
|
||||
# if defined(USART1_REMAP)
|
||||
# define USART_REMAP \
|
||||
do { \
|
||||
(IOMUX->REMAP |= IOMUX_REMAP_USART1_MUX); \
|
||||
} while (0)
|
||||
# elif defined(USART3_PARTIALREMAP)
|
||||
# define USART_REMAP \
|
||||
do { \
|
||||
(IOMUX->REMAP |= IOMUX_REMAP_USART3_MUX_MUX1); \
|
||||
} while (0)
|
||||
# elif defined(USART3_FULLREMAP)
|
||||
# define USART_REMAP \
|
||||
do { \
|
||||
(IOMUX->REMAP |= IOMUX_REMAP_USART3_MUX_MUX2); \
|
||||
} while (0)
|
||||
# endif
|
||||
#endif
|
||||
|
@ -103,9 +103,15 @@ bool spi_start_extended(spi_start_config_t *start_config) {
|
||||
roundedDivisor <<= 1;
|
||||
}
|
||||
|
||||
# if defined(AT32F415)
|
||||
if (roundedDivisor < 2 || roundedDivisor > 1024) {
|
||||
return false;
|
||||
}
|
||||
# else
|
||||
if (roundedDivisor < 2 || roundedDivisor > 256) {
|
||||
return false;
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(K20x) || defined(KL2x)
|
||||
@ -240,6 +246,59 @@ bool spi_start_extended(spi_start_config_t *start_config) {
|
||||
spiConfig.SSPCR0 |= SPI_SSPCR0_SPH; // Clock phase: sample on second edge transition
|
||||
break;
|
||||
}
|
||||
#elif defined(AT32F415)
|
||||
spiConfig.ctrl1 = 0;
|
||||
|
||||
if (lsbFirst) {
|
||||
spiConfig.ctrl1 |= SPI_CTRL1_LTF;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
spiConfig.ctrl1 |= SPI_CTRL1_CLKPHA;
|
||||
break;
|
||||
case 2:
|
||||
spiConfig.ctrl1 |= SPI_CTRL1_CLKPOL;
|
||||
break;
|
||||
case 3:
|
||||
spiConfig.ctrl1 |= SPI_CTRL1_CLKPHA | SPI_CTRL1_CLKPOL;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (roundedDivisor) {
|
||||
case 2:
|
||||
break;
|
||||
case 4:
|
||||
spiConfig.ctrl1 |= SPI_CTRL1_MDIV_0;
|
||||
break;
|
||||
case 8:
|
||||
spiConfig.ctrl1 |= SPI_CTRL1_MDIV_1;
|
||||
break;
|
||||
case 16:
|
||||
spiConfig.ctrl1 |= SPI_CTRL1_MDIV_1 | SPI_CTRL1_MDIV_0;
|
||||
break;
|
||||
case 32:
|
||||
spiConfig.ctrl1 |= SPI_CTRL1_MDIV_2;
|
||||
break;
|
||||
case 64:
|
||||
spiConfig.ctrl1 |= SPI_CTRL1_MDIV_2 | SPI_CTRL1_MDIV_0;
|
||||
break;
|
||||
case 128:
|
||||
spiConfig.ctrl1 |= SPI_CTRL1_MDIV_2 | SPI_CTRL1_MDIV_1;
|
||||
break;
|
||||
case 256:
|
||||
spiConfig.ctrl1 |= SPI_CTRL1_MDIV_2 | SPI_CTRL1_MDIV_1 | SPI_CTRL1_MDIV_0;
|
||||
break;
|
||||
case 512:
|
||||
spiConfig.ctrl2 |= SPI_CTRL1_MDIV_3;
|
||||
break;
|
||||
case 1024:
|
||||
spiConfig.ctrl2 |= SPI_CTRL1_MDIV_3;
|
||||
spiConfig.ctrl1 |= SPI_CTRL1_MDIV_0;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
spiConfig.cr1 = 0;
|
||||
|
||||
|
@ -33,7 +33,7 @@ static inline uint32_t detect_flash_size(void) {
|
||||
#elif defined(FLASH_SIZE)
|
||||
return FLASH_SIZE;
|
||||
#elif defined(FLASHSIZE_BASE)
|
||||
# if defined(QMK_MCU_SERIES_STM32F0XX) || defined(QMK_MCU_SERIES_STM32F1XX) || defined(QMK_MCU_SERIES_STM32F3XX) || defined(QMK_MCU_SERIES_STM32F4XX) || defined(QMK_MCU_SERIES_STM32G4XX) || defined(QMK_MCU_SERIES_STM32L0XX) || defined(QMK_MCU_SERIES_STM32L4XX) || defined(QMK_MCU_SERIES_GD32VF103)
|
||||
# if defined(QMK_MCU_SERIES_STM32F0XX) || defined(QMK_MCU_SERIES_STM32F1XX) || defined(QMK_MCU_SERIES_STM32F3XX) || defined(QMK_MCU_SERIES_STM32F4XX) || defined(QMK_MCU_SERIES_STM32G4XX) || defined(QMK_MCU_SERIES_STM32L0XX) || defined(QMK_MCU_SERIES_STM32L4XX) || defined(QMK_MCU_SERIES_AT32F415) || defined(QMK_MCU_SERIES_GD32VF103)
|
||||
return ((*(uint32_t *)FLASHSIZE_BASE) & 0xFFFFU) << 10U; // this register has the flash size in kB, so we convert it to bytes
|
||||
# elif defined(QMK_MCU_SERIES_STM32L1XX)
|
||||
# error This MCU family has an uncommon flash size register definition and has not been implemented. Perhaps try using the true EEPROM on the MCU instead?
|
||||
|
@ -16,6 +16,8 @@
|
||||
# define BACKING_STORE_WRITE_SIZE 4 // from hal_efl_lld.c
|
||||
# elif defined(QMK_MCU_FAMILY_WB32)
|
||||
# define BACKING_STORE_WRITE_SIZE 8 // from hal_efl_lld.c
|
||||
# elif defined(QMK_MCU_FAMILY_AT32)
|
||||
# define BACKING_STORE_WRITE_SIZE 2 // from hal_efl_lld.c
|
||||
# elif defined(QMK_MCU_FAMILY_STM32)
|
||||
# if defined(STM32_FLASH_LINE_SIZE) // from some family's stm32_registry.h file
|
||||
# define BACKING_STORE_WRITE_SIZE (STM32_FLASH_LINE_SIZE)
|
||||
|
@ -11,7 +11,7 @@
|
||||
/* Adapted from https://github.com/bigjosh/SimpleNeoPixelDemo/ */
|
||||
|
||||
#ifndef WS2812_BITBANG_NOP_FUDGE
|
||||
# if defined(STM32F0XX) || defined(STM32F1XX) || defined(GD32VF103) || defined(STM32F3XX) || defined(STM32F4XX) || defined(STM32L0XX) || defined(WB32F3G71xx) || defined(WB32FQ95xx)
|
||||
# if defined(STM32F0XX) || defined(STM32F1XX) || defined(GD32VF103) || defined(STM32F3XX) || defined(STM32F4XX) || defined(STM32L0XX) || defined(WB32F3G71xx) || defined(WB32FQ95xx) || defined(AT32F415)
|
||||
# define WS2812_BITBANG_NOP_FUDGE 0.4
|
||||
# else
|
||||
# if defined(RP2040)
|
||||
|
@ -40,6 +40,9 @@
|
||||
#if (STM32_DMA_SUPPORTS_DMAMUX == TRUE) && !defined(WS2812_PWM_DMAMUX_ID)
|
||||
# error "please consult your MCU's datasheet and specify in your config.h: #define WS2812_PWM_DMAMUX_ID STM32_DMAMUX1_TIM?_UP"
|
||||
#endif
|
||||
#if (AT32_DMA_SUPPORTS_DMAMUX == TRUE) && !defined(WS2812_PWM_DMAMUX_CHANNEL) && !defined(WS2812_PWM_DMAMUX_ID)
|
||||
# error "please consult your MCU's datasheet and specify in your config.h: #define WS2812_PWM_DMAMUX_CHANNEL 1, #define WS2812_PWM_DMAMUX_ID AT32_DMAMUX_TMR?_OVERFLOW"
|
||||
#endif
|
||||
|
||||
/* Summarize https://www.st.com/resource/en/application_note/an4013-stm32-crossseries-timer-overview-stmicroelectronics.pdf to
|
||||
* figure out if we are using a 32bit timer. This is needed to setup the DMA controller correctly.
|
||||
@ -269,6 +272,14 @@ typedef uint32_t ws2812_buffer_t;
|
||||
# define WS2812_PWM_DMA_PERIPHERAL_WIDTH STM32_DMA_CR_PSIZE_HWORD
|
||||
typedef uint16_t ws2812_buffer_t;
|
||||
# endif
|
||||
#elif defined(AT32F415)
|
||||
# define WS2812_PWM_DMA_MEMORY_WIDTH AT32_DMA_CCTRL_MWIDTH_BYTE
|
||||
# if defined(WS2812_PWM_TIMER_32BIT)
|
||||
# define WS2812_PWM_DMA_PERIPHERAL_WIDTH AT32_DMA_CCTRL_PWIDTH_WORD
|
||||
# else
|
||||
# define WS2812_PWM_DMA_PERIPHERAL_WIDTH AT32_DMA_CCTRL_PWIDTH_HWORD
|
||||
# endif
|
||||
typedef uint8_t ws2812_buffer_t;
|
||||
#else
|
||||
# define WS2812_PWM_DMA_MEMORY_WIDTH STM32_DMA_CR_MSIZE_BYTE
|
||||
# if defined(WS2812_PWM_TIMER_32BIT)
|
||||
@ -309,8 +320,13 @@ void ws2812_init(void) {
|
||||
[0 ... 3] = {.mode = PWM_OUTPUT_DISABLED, .callback = NULL}, // Channels default to disabled
|
||||
[WS2812_PWM_CHANNEL - 1] = {.mode = WS2812_PWM_OUTPUT_MODE, .callback = NULL}, // Turn on the channel we care about
|
||||
},
|
||||
#if defined(AT32F415)
|
||||
.ctrl2 = 0,
|
||||
.iden = AT32_TMR_IDEN_OVFDEN, // DMA on update event for next period
|
||||
#else
|
||||
.cr2 = 0,
|
||||
.dier = TIM_DIER_UDE, // DMA on update event for next period
|
||||
#endif
|
||||
};
|
||||
//#pragma GCC diagnostic pop // Restore command-line warning options
|
||||
|
||||
@ -321,6 +337,11 @@ void ws2812_init(void) {
|
||||
dmaStreamSetSource(WS2812_PWM_DMA_STREAM, ws2812_frame_buffer);
|
||||
dmaStreamSetDestination(WS2812_PWM_DMA_STREAM, &(WS2812_PWM_DRIVER.tim->CCR[WS2812_PWM_CHANNEL - 1])); // Ziel ist der An-Zeit im Cap-Comp-Register
|
||||
dmaStreamSetMode(WS2812_PWM_DMA_STREAM, WB32_DMA_CHCFG_HWHIF(WS2812_PWM_DMA_CHANNEL) | WB32_DMA_CHCFG_DIR_M2P | WB32_DMA_CHCFG_PSIZE_WORD | WB32_DMA_CHCFG_MSIZE_WORD | WB32_DMA_CHCFG_MINC | WB32_DMA_CHCFG_CIRC | WB32_DMA_CHCFG_TCIE | WB32_DMA_CHCFG_PL(3));
|
||||
#elif defined(AT32F415)
|
||||
dmaStreamAlloc(WS2812_PWM_DMA_STREAM - AT32_DMA_STREAM(0), 10, NULL, NULL);
|
||||
dmaStreamSetPeripheral(WS2812_PWM_DMA_STREAM, &(WS2812_PWM_DRIVER.tmr->CDT[WS2812_PWM_CHANNEL - 1])); // Ziel ist der An-Zeit im Cap-Comp-Register
|
||||
dmaStreamSetMemory0(WS2812_PWM_DMA_STREAM, ws2812_frame_buffer);
|
||||
dmaStreamSetMode(WS2812_PWM_DMA_STREAM, AT32_DMA_CCTRL_DTD_M2P | WS2812_PWM_DMA_PERIPHERAL_WIDTH | WS2812_PWM_DMA_MEMORY_WIDTH | AT32_DMA_CCTRL_MINCM | AT32_DMA_CCTRL_LM | AT32_DMA_CCTRL_CHPL(3));
|
||||
#else
|
||||
dmaStreamAlloc(WS2812_PWM_DMA_STREAM - STM32_DMA_STREAM(0), 10, NULL, NULL);
|
||||
dmaStreamSetPeripheral(WS2812_PWM_DMA_STREAM, &(WS2812_PWM_DRIVER.tim->CCR[WS2812_PWM_CHANNEL - 1])); // Ziel ist der An-Zeit im Cap-Comp-Register
|
||||
@ -335,6 +356,11 @@ void ws2812_init(void) {
|
||||
dmaSetRequestSource(WS2812_PWM_DMA_STREAM, WS2812_PWM_DMAMUX_ID);
|
||||
#endif
|
||||
|
||||
#if (AT32_DMA_SUPPORTS_DMAMUX == TRUE)
|
||||
// If the MCU has a DMAMUX we need to assign the correct resource
|
||||
dmaSetRequestSource(WS2812_PWM_DMA_STREAM, WS2812_PWM_DMAMUX_CHANNEL, WS2812_PWM_DMAMUX_ID);
|
||||
#endif
|
||||
|
||||
// Start DMA
|
||||
dmaStreamEnable(WS2812_PWM_DMA_STREAM);
|
||||
|
||||
|
@ -40,26 +40,53 @@
|
||||
|
||||
// Define SPI config speed
|
||||
// baudrate should target 3.2MHz
|
||||
#if defined(AT32F415)
|
||||
# if WS2812_SPI_DIVISOR == 2
|
||||
# define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (0)
|
||||
# elif WS2812_SPI_DIVISOR == 4
|
||||
# define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_0)
|
||||
# elif WS2812_SPI_DIVISOR == 8
|
||||
# define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_1)
|
||||
# elif WS2812_SPI_DIVISOR == 16 // default
|
||||
# define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_1 | SPI_CTRL1_MDIV_0)
|
||||
# elif WS2812_SPI_DIVISOR == 32
|
||||
# define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_2)
|
||||
# elif WS2812_SPI_DIVISOR == 64
|
||||
# define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_2 | SPI_CTRL1_MDIV_0)
|
||||
# elif WS2812_SPI_DIVISOR == 128
|
||||
# define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_2 | SPI_CTRL1_MDIV_1)
|
||||
# elif WS2812_SPI_DIVISOR == 256
|
||||
# define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_2 | SPI_CTRL1_MDIV_1 | SPI_CTRL1_MDIV_0)
|
||||
# elif WS2812_SPI_DIVISOR == 512
|
||||
# define WS2812_SPI_DIVISOR_CTRL2_MDIV_X (SPI_CTRL1_MDIV_3)
|
||||
# elif WS2812_SPI_DIVISOR == 1024
|
||||
# define WS2812_SPI_DIVISOR_CTRL2_MDIV_X (SPI_CTRL1_MDIV_3)
|
||||
# define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_0)
|
||||
# else
|
||||
# error "Configured WS2812_SPI_DIVISOR value is not supported at this time."
|
||||
# endif
|
||||
#else
|
||||
// F072 fpclk = 48MHz
|
||||
// 48/16 = 3Mhz
|
||||
#if WS2812_SPI_DIVISOR == 2
|
||||
# define WS2812_SPI_DIVISOR_CR1_BR_X (0)
|
||||
#elif WS2812_SPI_DIVISOR == 4
|
||||
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_0)
|
||||
#elif WS2812_SPI_DIVISOR == 8
|
||||
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_1)
|
||||
#elif WS2812_SPI_DIVISOR == 16 // default
|
||||
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_1 | SPI_CR1_BR_0)
|
||||
#elif WS2812_SPI_DIVISOR == 32
|
||||
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2)
|
||||
#elif WS2812_SPI_DIVISOR == 64
|
||||
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_0)
|
||||
#elif WS2812_SPI_DIVISOR == 128
|
||||
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_1)
|
||||
#elif WS2812_SPI_DIVISOR == 256
|
||||
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0)
|
||||
#else
|
||||
# error "Configured WS2812_SPI_DIVISOR value is not supported at this time."
|
||||
# if WS2812_SPI_DIVISOR == 2
|
||||
# define WS2812_SPI_DIVISOR_CR1_BR_X (0)
|
||||
# elif WS2812_SPI_DIVISOR == 4
|
||||
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_0)
|
||||
# elif WS2812_SPI_DIVISOR == 8
|
||||
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_1)
|
||||
# elif WS2812_SPI_DIVISOR == 16 // default
|
||||
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_1 | SPI_CR1_BR_0)
|
||||
# elif WS2812_SPI_DIVISOR == 32
|
||||
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2)
|
||||
# elif WS2812_SPI_DIVISOR == 64
|
||||
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_0)
|
||||
# elif WS2812_SPI_DIVISOR == 128
|
||||
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_1)
|
||||
# elif WS2812_SPI_DIVISOR == 256
|
||||
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0)
|
||||
# else
|
||||
# error "Configured WS2812_SPI_DIVISOR value is not supported at this time."
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Use SPI circular buffer
|
||||
@ -176,8 +203,16 @@ void ws2812_init(void) {
|
||||
NULL, // error_cb
|
||||
PAL_PORT(WS2812_DI_PIN),
|
||||
PAL_PAD(WS2812_DI_PIN),
|
||||
# if defined(AT32F415)
|
||||
WS2812_SPI_DIVISOR_CTRL1_MDIV_X,
|
||||
# if (WS2812_SPI_DIVISOR == 512 || WS2812_SPI_DIVISOR == 1024)
|
||||
WS2812_SPI_DIVISOR_CTRL2_MDIV_X,
|
||||
# endif
|
||||
0
|
||||
# else
|
||||
WS2812_SPI_DIVISOR_CR1_BR_X,
|
||||
0
|
||||
# endif
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -113,6 +113,8 @@ else ifeq ($(strip $(MCU_FAMILY)),STM32)
|
||||
$(UNSYNC_OUTPUT_CMD) && $(call EXEC_DFU_UTIL)
|
||||
else ifeq ($(strip $(MCU_FAMILY)),WB32)
|
||||
$(UNSYNC_OUTPUT_CMD) && $(call EXEC_WB32_DFU_UPDATER)
|
||||
else ifeq ($(strip $(MCU_FAMILY)),AT32)
|
||||
$(UNSYNC_OUTPUT_CMD) && $(call EXEC_DFU_UTIL)
|
||||
else ifeq ($(strip $(MCU_FAMILY)),GD32V)
|
||||
$(UNSYNC_OUTPUT_CMD) && $(call EXEC_DFU_UTIL)
|
||||
else
|
||||
|
@ -809,6 +809,40 @@ ifneq ($(findstring WB32FQ95, $(MCU)),)
|
||||
WB32_BOOTLOADER_ADDRESS ?= 0x1FFFE000
|
||||
endif
|
||||
|
||||
ifneq ($(findstring AT32F415, $(MCU)),)
|
||||
# Cortex version
|
||||
MCU = cortex-m4
|
||||
|
||||
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
|
||||
ARMV = 7
|
||||
|
||||
## chip/board settings
|
||||
# - the next two should match the directories in
|
||||
# <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)
|
||||
# OR
|
||||
# <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
|
||||
MCU_FAMILY = AT32
|
||||
MCU_SERIES = AT32F415
|
||||
|
||||
# Linker script to use
|
||||
# - it should exist either in <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/
|
||||
# or <keyboard_dir>/ld/
|
||||
MCU_LDSCRIPT ?= AT32F415xB
|
||||
|
||||
# Startup code to use
|
||||
# - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
|
||||
MCU_STARTUP ?= at32f415
|
||||
|
||||
# Board: it should exist either in <chibios>/os/hal/boards/,
|
||||
# <keyboard_dir>/boards/, or drivers/boards/
|
||||
BOARD ?= GENERIC_AT32_F415XX
|
||||
|
||||
USE_FPU ?= no
|
||||
|
||||
# Bootloader address for AT32 DFU
|
||||
AT32_BOOTLOADER_ADDRESS ?= 0x1FFFAC00
|
||||
endif
|
||||
|
||||
ifneq ($(findstring GD32VF103, $(MCU)),)
|
||||
# RISC-V
|
||||
MCU = risc-v
|
||||
|
@ -155,6 +155,10 @@ ifdef WB32_BOOTLOADER_ADDRESS
|
||||
OPT_DEFS += -DWB32_BOOTLOADER_ADDRESS=$(WB32_BOOTLOADER_ADDRESS)
|
||||
endif
|
||||
|
||||
ifdef AT32_BOOTLOADER_ADDRESS
|
||||
OPT_DEFS += -DAT32_BOOTLOADER_ADDRESS=$(AT32_BOOTLOADER_ADDRESS)
|
||||
endif
|
||||
|
||||
# Work out if we need to set up the include for the bootloader definitions
|
||||
ifneq ("$(wildcard $(KEYBOARD_PATH_5)/bootloader_defs.h)","")
|
||||
OPT_DEFS += -include $(KEYBOARD_PATH_5)/bootloader_defs.h
|
||||
|
@ -140,6 +140,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#ifdef OS_DETECTION_ENABLE
|
||||
# include "os_detection.h"
|
||||
#endif
|
||||
#if defined(LAYER_LOCK_ENABLE) && LAYER_LOCK_IDLE_TIMEOUT > 0
|
||||
# include "layer_lock.h"
|
||||
#endif // LAYER_LOCK_ENABLE
|
||||
|
||||
static uint32_t last_input_modification_time = 0;
|
||||
uint32_t last_input_activity_time(void) {
|
||||
@ -655,6 +658,10 @@ void quantum_task(void) {
|
||||
#ifdef SECURE_ENABLE
|
||||
secure_task();
|
||||
#endif
|
||||
|
||||
#if defined(LAYER_LOCK_ENABLE) && LAYER_LOCK_IDLE_TIMEOUT > 0
|
||||
layer_lock_task();
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \brief Main task that is repeatedly called as fast as possible. */
|
||||
|
@ -759,6 +759,7 @@ enum qk_keycode_defines {
|
||||
QK_TRI_LAYER_UPPER = 0x7C78,
|
||||
QK_REPEAT_KEY = 0x7C79,
|
||||
QK_ALT_REPEAT_KEY = 0x7C7A,
|
||||
QK_LAYER_LOCK = 0x7C7B,
|
||||
QK_KB_0 = 0x7E00,
|
||||
QK_KB_1 = 0x7E01,
|
||||
QK_KB_2 = 0x7E02,
|
||||
@ -1445,6 +1446,7 @@ enum qk_keycode_defines {
|
||||
TL_UPPR = QK_TRI_LAYER_UPPER,
|
||||
QK_REP = QK_REPEAT_KEY,
|
||||
QK_AREP = QK_ALT_REPEAT_KEY,
|
||||
QK_LLCK = QK_LAYER_LOCK,
|
||||
};
|
||||
|
||||
// Range Helpers
|
||||
@ -1501,7 +1503,7 @@ enum qk_keycode_defines {
|
||||
#define IS_UNDERGLOW_KEYCODE(code) ((code) >= QK_UNDERGLOW_TOGGLE && (code) <= QK_UNDERGLOW_SPEED_DOWN)
|
||||
#define IS_RGB_KEYCODE(code) ((code) >= RGB_MODE_PLAIN && (code) <= RGB_MODE_TWINKLE)
|
||||
#define IS_RGB_MATRIX_KEYCODE(code) ((code) >= QK_RGB_MATRIX_ON && (code) <= QK_RGB_MATRIX_SPEED_DOWN)
|
||||
#define IS_QUANTUM_KEYCODE(code) ((code) >= QK_BOOTLOADER && (code) <= QK_ALT_REPEAT_KEY)
|
||||
#define IS_QUANTUM_KEYCODE(code) ((code) >= QK_BOOTLOADER && (code) <= QK_LAYER_LOCK)
|
||||
#define IS_KB_KEYCODE(code) ((code) >= QK_KB_0 && (code) <= QK_KB_31)
|
||||
#define IS_USER_KEYCODE(code) ((code) >= QK_USER_0 && (code) <= QK_USER_31)
|
||||
|
||||
@ -1527,6 +1529,6 @@ enum qk_keycode_defines {
|
||||
#define UNDERGLOW_KEYCODE_RANGE QK_UNDERGLOW_TOGGLE ... QK_UNDERGLOW_SPEED_DOWN
|
||||
#define RGB_KEYCODE_RANGE RGB_MODE_PLAIN ... RGB_MODE_TWINKLE
|
||||
#define RGB_MATRIX_KEYCODE_RANGE QK_RGB_MATRIX_ON ... QK_RGB_MATRIX_SPEED_DOWN
|
||||
#define QUANTUM_KEYCODE_RANGE QK_BOOTLOADER ... QK_ALT_REPEAT_KEY
|
||||
#define QUANTUM_KEYCODE_RANGE QK_BOOTLOADER ... QK_LAYER_LOCK
|
||||
#define KB_KEYCODE_RANGE QK_KB_0 ... QK_KB_31
|
||||
#define USER_KEYCODE_RANGE QK_USER_0 ... QK_USER_31
|
||||
|
81
quantum/layer_lock.c
Normal file
81
quantum/layer_lock.c
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright 2022-2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "layer_lock.h"
|
||||
#include "quantum_keycodes.h"
|
||||
|
||||
#ifndef NO_ACTION_LAYER
|
||||
// The current lock state. The kth bit is on if layer k is locked.
|
||||
layer_state_t locked_layers = 0;
|
||||
|
||||
// Layer Lock timer to disable layer lock after X seconds inactivity
|
||||
# if defined(LAYER_LOCK_IDLE_TIMEOUT) && LAYER_LOCK_IDLE_TIMEOUT > 0
|
||||
uint32_t layer_lock_timer = 0;
|
||||
|
||||
void layer_lock_task(void) {
|
||||
if (locked_layers && timer_elapsed32(layer_lock_timer) > LAYER_LOCK_IDLE_TIMEOUT) {
|
||||
layer_lock_all_off();
|
||||
layer_lock_timer = timer_read32();
|
||||
}
|
||||
}
|
||||
# endif // LAYER_LOCK_IDLE_TIMEOUT > 0
|
||||
|
||||
bool is_layer_locked(uint8_t layer) {
|
||||
return locked_layers & ((layer_state_t)1 << layer);
|
||||
}
|
||||
|
||||
void layer_lock_invert(uint8_t layer) {
|
||||
const layer_state_t mask = (layer_state_t)1 << layer;
|
||||
if ((locked_layers & mask) == 0) { // Layer is being locked.
|
||||
# ifndef NO_ACTION_ONESHOT
|
||||
if (layer == get_oneshot_layer()) {
|
||||
reset_oneshot_layer(); // Reset so that OSL doesn't turn layer off.
|
||||
}
|
||||
# endif // NO_ACTION_ONESHOT
|
||||
layer_on(layer);
|
||||
# if defined(LAYER_LOCK_IDLE_TIMEOUT) && LAYER_LOCK_IDLE_TIMEOUT > 0
|
||||
layer_lock_timer = timer_read32();
|
||||
# endif // LAYER_LOCK_IDLE_TIMEOUT > 0
|
||||
} else { // Layer is being unlocked.
|
||||
layer_off(layer);
|
||||
}
|
||||
layer_lock_set_kb(locked_layers ^= mask);
|
||||
}
|
||||
|
||||
// Implement layer_lock_on/off by deferring to layer_lock_invert.
|
||||
void layer_lock_on(uint8_t layer) {
|
||||
if (!is_layer_locked(layer)) {
|
||||
layer_lock_invert(layer);
|
||||
}
|
||||
}
|
||||
|
||||
void layer_lock_off(uint8_t layer) {
|
||||
if (is_layer_locked(layer)) {
|
||||
layer_lock_invert(layer);
|
||||
}
|
||||
}
|
||||
|
||||
void layer_lock_all_off(void) {
|
||||
layer_and(~locked_layers);
|
||||
locked_layers = 0;
|
||||
layer_lock_set_kb(locked_layers);
|
||||
}
|
||||
|
||||
__attribute__((weak)) bool layer_lock_set_kb(layer_state_t locked_layers) {
|
||||
return layer_lock_set_user(locked_layers);
|
||||
}
|
||||
__attribute__((weak)) bool layer_lock_set_user(layer_state_t locked_layers) {
|
||||
return true;
|
||||
}
|
||||
#endif // NO_ACTION_LAYER
|
135
quantum/layer_lock.h
Normal file
135
quantum/layer_lock.h
Normal file
@ -0,0 +1,135 @@
|
||||
// Copyright 2022-2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @file layer_lock.h
|
||||
* @brief Layer Lock, a key to stay in the current layer.
|
||||
*
|
||||
* Overview
|
||||
* --------
|
||||
*
|
||||
* Layers are often accessed by holding a button, e.g. with a momentary layer
|
||||
* switch `MO(layer)` or layer tap `LT(layer, key)` key. But you may sometimes
|
||||
* want to "lock" or "toggle" the layer so that it stays on without having to
|
||||
* hold down a button. One way to do that is with a tap-toggle `TT` layer key,
|
||||
* but here is an alternative.
|
||||
*
|
||||
* This library implements a "Layer Lock key". When tapped, it "locks" the
|
||||
* highest layer to stay active, assuming the layer was activated by one of the
|
||||
* following keys:
|
||||
*
|
||||
* * `MO(layer)` momentary layer switch
|
||||
* * `LT(layer, key)` layer tap
|
||||
* * `OSL(layer)` one-shot layer
|
||||
* * `TT(layer)` layer tap toggle
|
||||
* * `LM(layer, mod)` layer-mod key (the layer is locked, but not the mods)
|
||||
*
|
||||
* Tapping the Layer Lock key again unlocks and turns off the layer.
|
||||
*
|
||||
* @note When a layer is "locked", other layer keys such as `TO(layer)` or
|
||||
* manually calling `layer_off(layer)` will override and unlock the layer.
|
||||
*
|
||||
* Configuration
|
||||
* -------------
|
||||
*
|
||||
* Optionally, a timeout may be defined so that Layer Lock disables
|
||||
* automatically if not keys are pressed for `LAYER_LOCK_IDLE_TIMEOUT`
|
||||
* milliseconds. Define `LAYER_LOCK_IDLE_TIMEOUT` in your config.h, for instance
|
||||
*
|
||||
* #define LAYER_LOCK_IDLE_TIMEOUT 60000 // Turn off after 60 seconds.
|
||||
*
|
||||
* and call `layer_lock_task()` from your `matrix_scan_user()` in keymap.c:
|
||||
*
|
||||
* void matrix_scan_user(void) {
|
||||
* layer_lock_task();
|
||||
* // Other tasks...
|
||||
* }
|
||||
*
|
||||
* For full documentation, see
|
||||
* <https://getreuer.info/posts/keyboards/layer-lock>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "action_layer.h"
|
||||
#include "action_util.h"
|
||||
|
||||
/**
|
||||
* Handler function for Layer Lock.
|
||||
*
|
||||
* In your keymap, define a custom keycode to use for Layer Lock. Then handle
|
||||
* Layer Lock from your `process_record_user` function by calling
|
||||
* `process_layer_lock`, passing your custom keycode for the `lock_keycode` arg:
|
||||
*
|
||||
* #include "features/layer_lock.h"
|
||||
*
|
||||
* bool process_record_user(uint16_t keycode, keyrecord_t* record) {
|
||||
* if (!process_layer_lock(keycode, record, LLOCK)) { return false; }
|
||||
* // Your macros ...
|
||||
*
|
||||
* return true;
|
||||
* }
|
||||
*/
|
||||
|
||||
#ifndef NO_ACTION_LAYER
|
||||
/** Returns true if `layer` is currently locked. */
|
||||
bool is_layer_locked(uint8_t layer);
|
||||
|
||||
/** Locks and turns on `layer`. */
|
||||
void layer_lock_on(uint8_t layer);
|
||||
|
||||
/** Unlocks and turns off `layer`. */
|
||||
void layer_lock_off(uint8_t layer);
|
||||
|
||||
/** Unlocks and turns off all locked layers. */
|
||||
void layer_lock_all_off(void);
|
||||
|
||||
/** Toggles whether `layer` is locked. */
|
||||
void layer_lock_invert(uint8_t layer);
|
||||
|
||||
/**
|
||||
* Optional callback that gets called when a layer is locked or unlocked.
|
||||
*
|
||||
* This is useful to represent the current lock state, e.g. by setting an LED or
|
||||
* playing a sound. In your keymap, define
|
||||
*
|
||||
* void layer_lock_set_user(layer_state_t locked_layers) {
|
||||
* // Do something like `set_led(is_layer_locked(NAV));`
|
||||
* }
|
||||
*
|
||||
* @param locked_layers Bitfield in which the kth bit represents whether the
|
||||
* kth layer is on.
|
||||
*/
|
||||
bool layer_lock_set_kb(layer_state_t locked_layers);
|
||||
bool layer_lock_set_user(layer_state_t locked_layers);
|
||||
|
||||
void layer_lock_task(void);
|
||||
#else // NO_ACTION_LAYER
|
||||
static inline bool is_layer_locked(uint8_t layer) {
|
||||
return false;
|
||||
}
|
||||
static inline void layer_lock_on(uint8_t layer) {}
|
||||
static inline void layer_lock_off(uint8_t layer) {}
|
||||
static inline void layer_lock_all_off(void) {}
|
||||
static inline void layer_lock_invert(uint8_t layer) {}
|
||||
static inline bool layer_lock_set_kb(layer_state_t locked_layers) {
|
||||
return true;
|
||||
}
|
||||
static inline bool layer_lock_set_user(layer_state_t locked_layers) {
|
||||
return true;
|
||||
}
|
||||
static inline void layer_lock_task(void) {}
|
||||
#endif // NO_ACTION_LAYER
|
95
quantum/process_keycode/process_layer_lock.c
Normal file
95
quantum/process_keycode/process_layer_lock.c
Normal file
@ -0,0 +1,95 @@
|
||||
// Copyright 2022-2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @file layer_lock.c
|
||||
* @brief Layer Lock implementation
|
||||
*
|
||||
* For full documentation, see
|
||||
* <https://getreuer.info/posts/keyboards/layer-lock>
|
||||
*/
|
||||
|
||||
#include "layer_lock.h"
|
||||
#include "process_layer_lock.h"
|
||||
#include "quantum_keycodes.h"
|
||||
#include "action_util.h"
|
||||
|
||||
// The current lock state. The kth bit is on if layer k is locked.
|
||||
extern layer_state_t locked_layers;
|
||||
#if defined(LAYER_LOCK_IDLE_TIMEOUT) && LAYER_LOCK_IDLE_TIMEOUT > 0
|
||||
extern uint32_t layer_lock_timer;
|
||||
#endif
|
||||
|
||||
// Handles an event on an `MO` or `TT` layer switch key.
|
||||
static bool handle_mo_or_tt(uint8_t layer, keyrecord_t* record) {
|
||||
if (is_layer_locked(layer)) {
|
||||
if (record->event.pressed) { // On press, unlock the layer.
|
||||
layer_lock_invert(layer);
|
||||
}
|
||||
return false; // Skip default handling.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool process_layer_lock(uint16_t keycode, keyrecord_t* record) {
|
||||
#ifndef NO_ACTION_LAYER
|
||||
# if defined(LAYER_LOCK_IDLE_TIMEOUT) && LAYER_LOCK_IDLE_TIMEOUT > 0
|
||||
layer_lock_timer = timer_read32();
|
||||
# endif // LAYER_LOCK_IDLE_TIMEOUT > 0
|
||||
|
||||
// The intention is that locked layers remain on. If something outside of
|
||||
// this feature turned any locked layers off, unlock them.
|
||||
if ((locked_layers & ~layer_state) != 0) {
|
||||
layer_lock_set_kb(locked_layers &= layer_state);
|
||||
}
|
||||
|
||||
if (keycode == QK_LAYER_LOCK) {
|
||||
if (record->event.pressed) { // The layer lock key was pressed.
|
||||
layer_lock_invert(get_highest_layer(layer_state));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (keycode) {
|
||||
case QK_MOMENTARY ... QK_MOMENTARY_MAX: // `MO(layer)` keys.
|
||||
return handle_mo_or_tt(QK_MOMENTARY_GET_LAYER(keycode), record);
|
||||
|
||||
case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX: // `TT(layer)`.
|
||||
return handle_mo_or_tt(QK_LAYER_TAP_TOGGLE_GET_LAYER(keycode), record);
|
||||
|
||||
case QK_LAYER_MOD ... QK_LAYER_MOD_MAX: { // `LM(layer, mod)`.
|
||||
uint8_t layer = QK_LAYER_MOD_GET_LAYER(keycode);
|
||||
if (is_layer_locked(layer)) {
|
||||
if (record->event.pressed) { // On press, unlock the layer.
|
||||
layer_lock_invert(layer);
|
||||
} else { // On release, clear the mods.
|
||||
clear_mods();
|
||||
send_keyboard_report();
|
||||
}
|
||||
return false; // Skip default handling.
|
||||
}
|
||||
} break;
|
||||
|
||||
# ifndef NO_ACTION_TAPPING
|
||||
case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: // `LT(layer, key)` keys.
|
||||
if (record->tap.count == 0 && !record->event.pressed && is_layer_locked(QK_LAYER_TAP_GET_LAYER(keycode))) {
|
||||
// Release event on a held layer-tap key where the layer is locked.
|
||||
return false; // Skip default handling so that layer stays on.
|
||||
}
|
||||
break;
|
||||
# endif // NO_ACTION_TAPPING
|
||||
}
|
||||
#endif // NO_ACTION_LAYER
|
||||
return true;
|
||||
}
|
69
quantum/process_keycode/process_layer_lock.h
Normal file
69
quantum/process_keycode/process_layer_lock.h
Normal file
@ -0,0 +1,69 @@
|
||||
// Copyright 2022-2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @file layer_lock.h
|
||||
* @brief Layer Lock, a key to stay in the current layer.
|
||||
*
|
||||
* Overview
|
||||
* --------
|
||||
*
|
||||
* Layers are often accessed by holding a button, e.g. with a momentary layer
|
||||
* switch `MO(layer)` or layer tap `LT(layer, key)` key. But you may sometimes
|
||||
* want to "lock" or "toggle" the layer so that it stays on without having to
|
||||
* hold down a button. One way to do that is with a tap-toggle `TT` layer key,
|
||||
* but here is an alternative.
|
||||
*
|
||||
* This library implements a "Layer Lock key". When tapped, it "locks" the
|
||||
* highest layer to stay active, assuming the layer was activated by one of the
|
||||
* following keys:
|
||||
*
|
||||
* * `MO(layer)` momentary layer switch
|
||||
* * `LT(layer, key)` layer tap
|
||||
* * `OSL(layer)` one-shot layer
|
||||
* * `TT(layer)` layer tap toggle
|
||||
* * `LM(layer, mod)` layer-mod key (the layer is locked, but not the mods)
|
||||
*
|
||||
* Tapping the Layer Lock key again unlocks and turns off the layer.
|
||||
*
|
||||
* @note When a layer is "locked", other layer keys such as `TO(layer)` or
|
||||
* manually calling `layer_off(layer)` will override and unlock the layer.
|
||||
*
|
||||
* Configuration
|
||||
* -------------
|
||||
*
|
||||
* Optionally, a timeout may be defined so that Layer Lock disables
|
||||
* automatically if not keys are pressed for `LAYER_LOCK_IDLE_TIMEOUT`
|
||||
* milliseconds. Define `LAYER_LOCK_IDLE_TIMEOUT` in your config.h, for instance
|
||||
*
|
||||
* #define LAYER_LOCK_IDLE_TIMEOUT 60000 // Turn off after 60 seconds.
|
||||
*
|
||||
* and call `layer_lock_task()` from your `matrix_scan_user()` in keymap.c:
|
||||
*
|
||||
* void matrix_scan_user(void) {
|
||||
* layer_lock_task();
|
||||
* // Other tasks...
|
||||
* }
|
||||
*
|
||||
* For full documentation, see
|
||||
* <https://getreuer.info/posts/keyboards/layer-lock>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "action.h"
|
||||
|
||||
bool process_layer_lock(uint16_t keycode, keyrecord_t* record);
|
@ -76,6 +76,10 @@
|
||||
# include "process_unicode_common.h"
|
||||
#endif
|
||||
|
||||
#ifdef LAYER_LOCK_ENABLE
|
||||
# include "process_layer_lock.h"
|
||||
#endif // LAYER_LOCK_ENABLE
|
||||
|
||||
#ifdef AUDIO_ENABLE
|
||||
# ifndef GOODBYE_SONG
|
||||
# define GOODBYE_SONG SONG(GOODBYE_SOUND)
|
||||
@ -400,6 +404,9 @@ bool process_record_quantum(keyrecord_t *record) {
|
||||
#ifdef TRI_LAYER_ENABLE
|
||||
process_tri_layer(keycode, record) &&
|
||||
#endif
|
||||
#ifdef LAYER_LOCK_ENABLE
|
||||
process_layer_lock(keycode, record) &&
|
||||
#endif
|
||||
#ifdef BLUETOOTH_ENABLE
|
||||
process_connection(keycode, record) &&
|
||||
#endif
|
||||
|
@ -240,6 +240,10 @@ extern layer_state_t layer_state;
|
||||
# include "os_detection.h"
|
||||
#endif
|
||||
|
||||
#ifdef LAYER_LOCK_ENABLE
|
||||
# include "layer_lock.h"
|
||||
#endif // LAYER_LOCK_ENABLE
|
||||
|
||||
void set_single_default_layer(uint8_t default_layer);
|
||||
void set_single_persistent_default_layer(uint8_t default_layer);
|
||||
|
||||
|
8
tests/layer_lock/config.h
Normal file
8
tests/layer_lock/config.h
Normal file
@ -0,0 +1,8 @@
|
||||
// Copyright 2021 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "test_common.h"
|
||||
|
||||
#define LAYER_LOCK_IDLE_TIMEOUT 1000
|
8
tests/layer_lock/test.mk
Normal file
8
tests/layer_lock/test.mk
Normal file
@ -0,0 +1,8 @@
|
||||
# Copyright 2021 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# --------------------------------------------------------------------------------
|
||||
# Keep this file, even if it is empty, as a marker that this folder contains tests
|
||||
# --------------------------------------------------------------------------------
|
||||
|
||||
LAYER_LOCK_ENABLE = yes
|
284
tests/layer_lock/test_layer_lock.cpp
Normal file
284
tests/layer_lock/test_layer_lock.cpp
Normal file
@ -0,0 +1,284 @@
|
||||
// Copyright 2021 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "keycodes.h"
|
||||
#include "test_common.hpp"
|
||||
|
||||
using testing::_;
|
||||
|
||||
class LayerLock : public TestFixture {};
|
||||
|
||||
TEST_F(LayerLock, LayerLockState) {
|
||||
TestDriver driver;
|
||||
KeymapKey key_a = KeymapKey(0, 0, 0, KC_A);
|
||||
KeymapKey key_b = KeymapKey(1, 0, 0, KC_B);
|
||||
KeymapKey key_c = KeymapKey(2, 0, 0, KC_C);
|
||||
KeymapKey key_d = KeymapKey(3, 0, 0, KC_C);
|
||||
|
||||
set_keymap({key_a, key_b, key_c, key_d});
|
||||
|
||||
EXPECT_FALSE(is_layer_locked(1));
|
||||
EXPECT_FALSE(is_layer_locked(2));
|
||||
EXPECT_FALSE(is_layer_locked(3));
|
||||
|
||||
layer_lock_invert(1); // Layer 1: unlocked -> locked
|
||||
layer_lock_on(2); // Layer 2: unlocked -> locked
|
||||
layer_lock_off(3); // Layer 3: stays unlocked
|
||||
|
||||
// Layers 1 and 2 are now on.
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
EXPECT_TRUE(layer_state_is(2));
|
||||
// Layers 1 and 2 are now locked.
|
||||
EXPECT_TRUE(is_layer_locked(1));
|
||||
EXPECT_TRUE(is_layer_locked(2));
|
||||
EXPECT_FALSE(is_layer_locked(3));
|
||||
|
||||
layer_lock_invert(1); // Layer 1: locked -> unlocked
|
||||
layer_lock_on(2); // Layer 2: stays locked
|
||||
layer_lock_on(3); // Layer 3: unlocked -> locked
|
||||
|
||||
EXPECT_FALSE(layer_state_is(1));
|
||||
EXPECT_TRUE(layer_state_is(2));
|
||||
EXPECT_TRUE(layer_state_is(3));
|
||||
EXPECT_FALSE(is_layer_locked(1));
|
||||
EXPECT_TRUE(is_layer_locked(2));
|
||||
EXPECT_TRUE(is_layer_locked(3));
|
||||
|
||||
layer_lock_invert(1); // Layer 1: unlocked -> locked
|
||||
layer_lock_off(2); // Layer 2: locked -> unlocked
|
||||
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
EXPECT_FALSE(layer_state_is(2));
|
||||
EXPECT_TRUE(layer_state_is(3));
|
||||
EXPECT_TRUE(is_layer_locked(1));
|
||||
EXPECT_FALSE(is_layer_locked(2));
|
||||
EXPECT_TRUE(is_layer_locked(3));
|
||||
|
||||
layer_lock_all_off(); // Layers 1 and 3: locked -> unlocked
|
||||
|
||||
EXPECT_FALSE(layer_state_is(1));
|
||||
EXPECT_FALSE(layer_state_is(2));
|
||||
EXPECT_FALSE(layer_state_is(3));
|
||||
EXPECT_FALSE(is_layer_locked(1));
|
||||
EXPECT_FALSE(is_layer_locked(2));
|
||||
EXPECT_FALSE(is_layer_locked(3));
|
||||
}
|
||||
|
||||
TEST_F(LayerLock, LayerLockMomentaryTest) {
|
||||
TestDriver driver;
|
||||
KeymapKey key_layer = KeymapKey(0, 0, 0, MO(1));
|
||||
KeymapKey key_a = KeymapKey(0, 1, 0, KC_A);
|
||||
KeymapKey key_trns = KeymapKey(1, 0, 0, KC_TRNS);
|
||||
KeymapKey key_ll = KeymapKey(1, 1, 0, QK_LAYER_LOCK);
|
||||
|
||||
set_keymap({key_layer, key_a, key_trns, key_ll});
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
key_layer.press();
|
||||
run_one_scan_loop();
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
EXPECT_FALSE(is_layer_locked(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
tap_key(key_ll);
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
EXPECT_TRUE(is_layer_locked(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
key_layer.release();
|
||||
run_one_scan_loop();
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
EXPECT_TRUE(is_layer_locked(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Pressing Layer Lock again unlocks the lock.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
key_ll.press();
|
||||
run_one_scan_loop();
|
||||
EXPECT_FALSE(layer_state_is(1));
|
||||
EXPECT_FALSE(is_layer_locked(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_F(LayerLock, LayerLockLayerTapTest) {
|
||||
TestDriver driver;
|
||||
KeymapKey key_layer = KeymapKey(0, 0, 0, LT(1, KC_B));
|
||||
KeymapKey key_a = KeymapKey(0, 1, 0, KC_A);
|
||||
KeymapKey key_trns = KeymapKey(1, 0, 0, KC_TRNS);
|
||||
KeymapKey key_ll = KeymapKey(1, 1, 0, QK_LAYER_LOCK);
|
||||
|
||||
set_keymap({key_layer, key_a, key_trns, key_ll});
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
key_layer.press();
|
||||
idle_for(TAPPING_TERM);
|
||||
run_one_scan_loop();
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
tap_key(key_ll);
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
EXPECT_TRUE(is_layer_locked(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Pressing Layer Lock again unlocks the lock.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
key_ll.press();
|
||||
run_one_scan_loop();
|
||||
EXPECT_FALSE(layer_state_is(1));
|
||||
EXPECT_FALSE(is_layer_locked(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_F(LayerLock, LayerLockOneshotTapTest) {
|
||||
TestDriver driver;
|
||||
KeymapKey key_layer = KeymapKey(0, 0, 0, OSL(1));
|
||||
KeymapKey key_a = KeymapKey(0, 1, 0, KC_A);
|
||||
KeymapKey key_trns = KeymapKey(1, 0, 0, KC_TRNS);
|
||||
KeymapKey key_ll = KeymapKey(1, 1, 0, QK_LAYER_LOCK);
|
||||
|
||||
set_keymap({key_layer, key_a, key_trns, key_ll});
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
tap_key(key_layer);
|
||||
run_one_scan_loop();
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
tap_key(key_ll);
|
||||
run_one_scan_loop();
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
EXPECT_TRUE(is_layer_locked(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Pressing Layer Lock again unlocks the lock.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
key_ll.press();
|
||||
run_one_scan_loop();
|
||||
EXPECT_FALSE(layer_state_is(1));
|
||||
EXPECT_FALSE(is_layer_locked(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_F(LayerLock, LayerLockOneshotHoldTest) {
|
||||
TestDriver driver;
|
||||
KeymapKey key_layer = KeymapKey(0, 0, 0, OSL(1));
|
||||
KeymapKey key_a = KeymapKey(0, 1, 0, KC_A);
|
||||
KeymapKey key_trns = KeymapKey(1, 0, 0, KC_TRNS);
|
||||
KeymapKey key_ll = KeymapKey(1, 1, 0, QK_LAYER_LOCK);
|
||||
|
||||
set_keymap({key_layer, key_a, key_trns, key_ll});
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
key_layer.press();
|
||||
idle_for(TAPPING_TERM);
|
||||
run_one_scan_loop();
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
tap_key(key_ll);
|
||||
run_one_scan_loop();
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
key_layer.release();
|
||||
run_one_scan_loop();
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
EXPECT_TRUE(is_layer_locked(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Pressing Layer Lock again unlocks the lock.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
key_ll.press();
|
||||
run_one_scan_loop();
|
||||
EXPECT_FALSE(layer_state_is(1));
|
||||
EXPECT_FALSE(is_layer_locked(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_F(LayerLock, LayerLockTimeoutTest) {
|
||||
TestDriver driver;
|
||||
KeymapKey key_layer = KeymapKey(0, 0, 0, MO(1));
|
||||
KeymapKey key_a = KeymapKey(0, 1, 0, KC_A);
|
||||
KeymapKey key_trns = KeymapKey(1, 0, 0, KC_TRNS);
|
||||
KeymapKey key_ll = KeymapKey(1, 1, 0, QK_LAYER_LOCK);
|
||||
|
||||
set_keymap({key_layer, key_a, key_trns, key_ll});
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
key_layer.press();
|
||||
run_one_scan_loop();
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
tap_key(key_ll);
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
key_layer.release();
|
||||
run_one_scan_loop();
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
EXPECT_TRUE(is_layer_locked(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
idle_for(LAYER_LOCK_IDLE_TIMEOUT);
|
||||
run_one_scan_loop();
|
||||
EXPECT_FALSE(layer_state_is(1));
|
||||
EXPECT_FALSE(is_layer_locked(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_F(LayerLock, ToKeyOverridesLayerLock) {
|
||||
TestDriver driver;
|
||||
KeymapKey key_layer = KeymapKey(0, 0, 0, MO(1));
|
||||
KeymapKey key_to0 = KeymapKey(1, 0, 0, TO(0));
|
||||
KeymapKey key_ll = KeymapKey(1, 1, 0, QK_LAYER_LOCK);
|
||||
|
||||
set_keymap({key_layer, key_to0, key_ll});
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
layer_lock_on(1);
|
||||
run_one_scan_loop();
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
EXPECT_TRUE(is_layer_locked(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
tap_key(key_to0); // TO(0) overrides Layer Lock and unlocks layer 1.
|
||||
EXPECT_FALSE(layer_state_is(1));
|
||||
EXPECT_FALSE(is_layer_locked(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_F(LayerLock, LayerClearOverridesLayerLock) {
|
||||
TestDriver driver;
|
||||
KeymapKey key_layer = KeymapKey(0, 0, 0, MO(1));
|
||||
KeymapKey key_a = KeymapKey(0, 1, 0, KC_A);
|
||||
KeymapKey key_ll = KeymapKey(1, 1, 0, QK_LAYER_LOCK);
|
||||
|
||||
set_keymap({key_layer, key_a, key_ll});
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
layer_lock_on(1);
|
||||
run_one_scan_loop();
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
EXPECT_TRUE(is_layer_locked(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
EXPECT_REPORT(driver, (KC_A));
|
||||
layer_clear(); // layer_clear() overrides Layer Lock and unlocks layer 1.
|
||||
key_a.press();
|
||||
run_one_scan_loop();
|
||||
EXPECT_FALSE(layer_state_is(1));
|
||||
EXPECT_FALSE(is_layer_locked(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
@ -699,6 +699,7 @@ std::map<uint16_t, std::string> KEYCODE_ID_TABLE = {
|
||||
{QK_TRI_LAYER_UPPER, "QK_TRI_LAYER_UPPER"},
|
||||
{QK_REPEAT_KEY, "QK_REPEAT_KEY"},
|
||||
{QK_ALT_REPEAT_KEY, "QK_ALT_REPEAT_KEY"},
|
||||
{QK_LAYER_LOCK, "QK_LAYER_LOCK"},
|
||||
{QK_KB_0, "QK_KB_0"},
|
||||
{QK_KB_1, "QK_KB_1"},
|
||||
{QK_KB_2, "QK_KB_2"},
|
||||
|
Loading…
Reference in New Issue
Block a user