mirror of
https://github.com/qmk/qmk_firmware.git
synced 2024-11-22 03:19:24 +00:00
Compare commits
8 Commits
ae1c5abd18
...
8fad1a5b53
Author | SHA1 | Date | |
---|---|---|---|
|
8fad1a5b53 | ||
|
9e9b4acbde | ||
|
57be487161 | ||
|
968a611476 | ||
|
65a8a5ff69 | ||
|
c7a04bd930 | ||
|
105e83bbe3 | ||
|
d1fa591b49 |
@ -153,20 +153,26 @@ qmk cd
|
||||
|
||||
This command allows for searching through keyboard/keymap targets, filtering by specific criteria. `info.json` and `rules.mk` files contribute to the search data, as well as keymap configurations, and the results can be filtered using "dotty" syntax matching the overall `info.json` file format.
|
||||
|
||||
For example, one could search for all keyboards using STM32F411:
|
||||
For example, one could search for all keyboards powered by the STM32F411 microcontroller:
|
||||
|
||||
```
|
||||
qmk find -f 'processor=STM32F411'
|
||||
qmk find -f 'processor==STM32F411'
|
||||
```
|
||||
|
||||
...and one can further constrain the list to keyboards using STM32F411 as well as rgb_matrix support:
|
||||
The list can be further constrained by passing additional filter expressions:
|
||||
|
||||
```
|
||||
qmk find -f 'processor=STM32F411' -f 'features.rgb_matrix=true'
|
||||
qmk find -f 'processor==STM32F411' -f 'features.rgb_matrix==true'
|
||||
```
|
||||
|
||||
The following filter expressions are also supported:
|
||||
The following filter expressions are supported:
|
||||
|
||||
- `key == value`: Match targets where `key` is equal to `value`. May include wildcards such as `*` and `?`.
|
||||
- `key != value`: Match targets where `key` is not `value`. May include wildcards such as `*` and `?`.
|
||||
- `key < value`: Match targets where `key` is a number less than `value`.
|
||||
- `key > value`: Match targets where `key` is a number greater than `value`.
|
||||
- `key <= value`: Match targets where `key` is a number less than or equal to `value`.
|
||||
- `key >= value`: Match targets where `key` is a number greater than or equal to `value`.
|
||||
- `exists(key)`: Match targets where `key` is present.
|
||||
- `absent(key)`: Match targets where `key` is not present.
|
||||
- `contains(key, value)`: Match targets where `key` contains `value`. Can be used for strings, arrays and object keys.
|
||||
@ -175,7 +181,7 @@ The following filter expressions are also supported:
|
||||
You can also list arbitrary values for each matched target with `--print`:
|
||||
|
||||
```
|
||||
qmk find -f 'processor=STM32F411' -p 'keyboard_name' -p 'features.rgb_matrix'
|
||||
qmk find -f 'processor==STM32F411' -p 'keyboard_name' -p 'features.rgb_matrix'
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
|
@ -156,25 +156,25 @@ For more on the `info.json` files, see [`info.json` Format](reference_info_json)
|
||||
|
||||
The Configurator's API uses the layout macro and the JSON file we've given it to create a visual representation of the keyboard that has each visual object tied to a specific key, in sequence:
|
||||
|
||||
key in layout macro | JSON object used
|
||||
:---: | :----
|
||||
k00 | {"label":"Num Lock", "x":0, "y":0}
|
||||
k01 | {"label":"/", "x":1, "y":0}
|
||||
k02 | {"label":"*", "x":2, "y":0}
|
||||
k03 | {"label":"-", "x":3, "y":0}
|
||||
k10 | {"label":"7", "x":0, "y":1}
|
||||
k11 | {"label":"8", "x":1, "y":1}
|
||||
k12 | {"label":"9", "x":2, "y":1}
|
||||
k13 | {"label":"+", "x":3, "y":1, "h":2}
|
||||
k20 | {"label":"4", "x":0, "y":2}
|
||||
k21 | {"label":"5", "x":1, "y":2}
|
||||
k22 | {"label":"6", "x":2, "y":2}
|
||||
k30 | {"label":"1", "x":0, "y":3}
|
||||
k31 | {"label":"2", "x":1, "y":3}
|
||||
k32 | {"label":"3", "x":2, "y":3}
|
||||
k33 | {"label":"Enter", "x":3, "y":3, "h":2}
|
||||
k40 | {"label":"0", "x":0, "y":4, "w":2}
|
||||
k42 | {"label":".", "x":2, "y":4}
|
||||
| Key in layout macro | JSON object used |
|
||||
| ------------------- | ---------------------------------------- |
|
||||
| k00 | `{"label":"Num Lock", "x":0, "y":0}` |
|
||||
| k01 | `{"label":"/", "x":1, "y":0}` |
|
||||
| k02 | `{"label":"*", "x":2, "y":0}` |
|
||||
| k03 | `{"label":"-", "x":3, "y":0}` |
|
||||
| k10 | `{"label":"7", "x":0, "y":1}` |
|
||||
| k11 | `{"label":"8", "x":1, "y":1}` |
|
||||
| k12 | `{"label":"9", "x":2, "y":1}` |
|
||||
| k13 | `{"label":"+", "x":3, "y":1, "h":2}` |
|
||||
| k20 | `{"label":"4", "x":0, "y":2}` |
|
||||
| k21 | `{"label":"5", "x":1, "y":2}` |
|
||||
| k22 | `{"label":"6", "x":2, "y":2}` |
|
||||
| k30 | `{"label":"1", "x":0, "y":3}` |
|
||||
| k31 | `{"label":"2", "x":1, "y":3}` |
|
||||
| k32 | `{"label":"3", "x":2, "y":3}` |
|
||||
| k33 | `{"label":"Enter", "x":3, "y":3, "h":2}` |
|
||||
| k40 | `{"label":"0", "x":0, "y":4, "w":2}` |
|
||||
| k42 | `{"label":".", "x":2, "y":4}` |
|
||||
|
||||
When a user selects the top-left key in the Configurator, and assigns Num Lock to it, the Configurator builds a keymap file with `KC_NUM` as the first key, and so on as the keymap is built. The `label` keys are not used; they are only for the user's reference in identifying specific keys when debugging the `info.json` file.
|
||||
|
||||
|
@ -239,11 +239,11 @@ def _filter_keymap_targets(target_list: List[KeyboardKeymapDesc], filters: List[
|
||||
valid_targets = parallel_map(_load_keymap_info, target_list)
|
||||
|
||||
function_re = re.compile(r'^(?P<function>[a-zA-Z]+)\((?P<key>[a-zA-Z0-9_\.]+)(,\s*(?P<value>[^#]+))?\)$')
|
||||
equals_re = re.compile(r'^(?P<key>[a-zA-Z0-9_\.]+)\s*=\s*(?P<value>[^#]+)$')
|
||||
comparison_re = re.compile(r'^(?P<key>[a-zA-Z0-9_\.]+)\s*(?P<op>[\<\>\!=]=|\<|\>)\s*(?P<value>[^#]+)$')
|
||||
|
||||
for filter_expr in filters:
|
||||
function_match = function_re.match(filter_expr)
|
||||
equals_match = equals_re.match(filter_expr)
|
||||
comparison_match = comparison_re.match(filter_expr)
|
||||
|
||||
if function_match is not None:
|
||||
func_name = function_match.group('function').lower()
|
||||
@ -259,23 +259,43 @@ def _filter_keymap_targets(target_list: List[KeyboardKeymapDesc], filters: List[
|
||||
value_str = f", {{fg_cyan}}{value}{{fg_reset}}" if value is not None else ""
|
||||
cli.log.info(f'Filtering on condition: {{fg_green}}{func_name}{{fg_reset}}({{fg_cyan}}{key}{{fg_reset}}{value_str})...')
|
||||
|
||||
elif equals_match is not None:
|
||||
key = equals_match.group('key')
|
||||
value = equals_match.group('value')
|
||||
cli.log.info(f'Filtering on condition: {{fg_cyan}}{key}{{fg_reset}} == {{fg_cyan}}{value}{{fg_reset}}...')
|
||||
elif comparison_match is not None:
|
||||
key = comparison_match.group('key')
|
||||
op = comparison_match.group('op')
|
||||
value = comparison_match.group('value')
|
||||
cli.log.info(f'Filtering on condition: {{fg_cyan}}{key}{{fg_reset}} {op} {{fg_cyan}}{value}{{fg_reset}}...')
|
||||
|
||||
def _make_filter(k, v):
|
||||
def _make_filter(k, o, v):
|
||||
expr = fnmatch.translate(v)
|
||||
rule = re.compile(f'^{expr}$', re.IGNORECASE)
|
||||
|
||||
def f(e: KeyboardKeymapDesc):
|
||||
lhs = e.dotty.get(k)
|
||||
lhs = str(False if lhs is None else lhs)
|
||||
return rule.search(lhs) is not None
|
||||
rhs = v
|
||||
|
||||
if o in ['<', '>', '<=', '>=']:
|
||||
lhs = int(False if lhs is None else lhs)
|
||||
rhs = int(rhs)
|
||||
|
||||
if o == '<':
|
||||
return lhs < rhs
|
||||
elif o == '>':
|
||||
return lhs > rhs
|
||||
elif o == '<=':
|
||||
return lhs <= rhs
|
||||
elif o == '>=':
|
||||
return lhs >= rhs
|
||||
else:
|
||||
lhs = str(False if lhs is None else lhs)
|
||||
|
||||
if o == '!=':
|
||||
return rule.search(lhs) is None
|
||||
elif o == '==':
|
||||
return rule.search(lhs) is not None
|
||||
|
||||
return f
|
||||
|
||||
valid_targets = filter(_make_filter(key, value), valid_targets)
|
||||
valid_targets = filter(_make_filter(key, op, value), valid_targets)
|
||||
else:
|
||||
cli.log.warning(f'Unrecognized filter expression: {filter_expr}')
|
||||
continue
|
||||
|
@ -390,7 +390,7 @@ def test_find_contains():
|
||||
def test_find_multiple_conditions():
|
||||
# this is intended to match at least 'crkbd/rev1'
|
||||
result = check_subcommand(
|
||||
'find', '-f', 'exists(rgb_matrix.split_count)', '-f', 'contains(matrix_pins.cols, B1)', '-f', 'length(matrix_pins.cols, 6)', '-f', 'absent(eeprom.driver)', '-f', 'ws2812.pin=D3', '-p', 'rgb_matrix.split_count', '-p', 'matrix_pins.cols', '-p',
|
||||
'find', '-f', 'exists(rgb_matrix.split_count)', '-f', 'contains(matrix_pins.cols, B1)', '-f', 'length(matrix_pins.cols, 6)', '-f', 'absent(eeprom.driver)', '-f', 'ws2812.pin == D3', '-p', 'rgb_matrix.split_count', '-p', 'matrix_pins.cols', '-p',
|
||||
'eeprom.driver', '-p', 'ws2812.pin'
|
||||
)
|
||||
check_returncode(result)
|
||||
|
@ -21,3 +21,13 @@
|
||||
#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP
|
||||
# define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE
|
||||
#endif
|
||||
|
||||
#ifdef WEAR_LEVELING_EMBEDDED_FLASH
|
||||
# ifndef WEAR_LEVELING_EFL_FIRST_SECTOR
|
||||
# ifdef BOOTLOADER_TINYUF2
|
||||
# define WEAR_LEVELING_EFL_FIRST_SECTOR 3
|
||||
# else
|
||||
# define WEAR_LEVELING_EFL_FIRST_SECTOR 1
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
@ -21,3 +21,13 @@
|
||||
#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP
|
||||
# define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE
|
||||
#endif
|
||||
|
||||
#ifdef WEAR_LEVELING_EMBEDDED_FLASH
|
||||
# ifndef WEAR_LEVELING_EFL_FIRST_SECTOR
|
||||
# ifdef BOOTLOADER_TINYUF2
|
||||
# define WEAR_LEVELING_EFL_FIRST_SECTOR 3
|
||||
# else
|
||||
# define WEAR_LEVELING_EFL_FIRST_SECTOR 1
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
@ -17,3 +17,13 @@
|
||||
#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP
|
||||
# define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE
|
||||
#endif
|
||||
|
||||
#ifdef WEAR_LEVELING_EMBEDDED_FLASH
|
||||
# ifndef WEAR_LEVELING_EFL_FIRST_SECTOR
|
||||
# ifdef BOOTLOADER_TINYUF2
|
||||
# define WEAR_LEVELING_EFL_FIRST_SECTOR 3
|
||||
# else
|
||||
# define WEAR_LEVELING_EFL_FIRST_SECTOR 1
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
@ -5,6 +5,9 @@
|
||||
*/
|
||||
|
||||
f4xx_flash_size = 256k;
|
||||
f4xx_ram_size = 64k;
|
||||
f4xx_ram1_size = 64k;
|
||||
f4xx_ram2_size = 0;
|
||||
f4xx_ram4_size = 0;
|
||||
f4xx_ram5_size = 0;
|
||||
|
||||
INCLUDE stm32f4xx_common.ld
|
||||
|
@ -5,6 +5,9 @@
|
||||
*/
|
||||
|
||||
f4xx_flash_size = 256k;
|
||||
f4xx_ram_size = 64k;
|
||||
f4xx_ram1_size = 64k;
|
||||
f4xx_ram2_size = 0;
|
||||
f4xx_ram4_size = 0;
|
||||
f4xx_ram5_size = 0;
|
||||
|
||||
INCLUDE stm32f4xx_tinyuf2_common.ld
|
||||
|
@ -5,6 +5,9 @@
|
||||
*/
|
||||
|
||||
f4xx_flash_size = 512k;
|
||||
f4xx_ram_size = 96k;
|
||||
f4xx_ram1_size = 96k;
|
||||
f4xx_ram2_size = 0;
|
||||
f4xx_ram4_size = 0;
|
||||
f4xx_ram5_size = 0;
|
||||
|
||||
INCLUDE stm32f4xx_common.ld
|
||||
|
@ -5,6 +5,9 @@
|
||||
*/
|
||||
|
||||
f4xx_flash_size = 512k;
|
||||
f4xx_ram_size = 96k;
|
||||
f4xx_ram1_size = 96k;
|
||||
f4xx_ram2_size = 0;
|
||||
f4xx_ram4_size = 0;
|
||||
f4xx_ram5_size = 0;
|
||||
|
||||
INCLUDE stm32f4xx_tinyuf2_common.ld
|
||||
|
@ -1,86 +1,13 @@
|
||||
/*
|
||||
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* STM32F405xG memory setup.
|
||||
* Note: Use of ram1 and ram2 is mutually exclusive with use of ram0.
|
||||
* Copyright 2006..2018 Giovanni Di Sirio
|
||||
* Copyright 2022 QMK contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
MEMORY
|
||||
{
|
||||
flash0 (rx) : org = 0x08000000, len = 16k /* Sector 0 - Init code as ROM bootloader assumes application starts here */
|
||||
flash1 (rx) : org = 0x08004000, len = 16k /* Sector 1 - Emulated eeprom */
|
||||
flash2 (rx) : org = 0x08008000, len = 1M - 32k /* Sector 2..6 - Rest of firmware */
|
||||
flash3 (rx) : org = 0x00000000, len = 0
|
||||
flash4 (rx) : org = 0x00000000, len = 0
|
||||
flash5 (rx) : org = 0x00000000, len = 0
|
||||
flash6 (rx) : org = 0x00000000, len = 0
|
||||
flash7 (rx) : org = 0x00000000, len = 0
|
||||
ram0 (wx) : org = 0x20000000, len = 128k /* SRAM1 + SRAM2 */
|
||||
ram1 (wx) : org = 0x20000000, len = 112k /* SRAM1 */
|
||||
ram2 (wx) : org = 0x2001C000, len = 16k /* SRAM2 */
|
||||
ram3 (wx) : org = 0x00000000, len = 0
|
||||
ram4 (wx) : org = 0x10000000, len = 64k /* CCM SRAM */
|
||||
ram5 (wx) : org = 0x40024000, len = 4k /* BCKP SRAM */
|
||||
ram6 (wx) : org = 0x00000000, len = 0
|
||||
ram7 (wx) : org = 0x00000000, len = 0
|
||||
}
|
||||
|
||||
/* For each data/text section two region are defined, a virtual region
|
||||
and a load region (_LMA suffix).*/
|
||||
f4xx_flash_size = 1M;
|
||||
f4xx_ram1_size = 112k;
|
||||
f4xx_ram2_size = 16k;
|
||||
f4xx_ram4_size = 64k;
|
||||
f4xx_ram5_size = 4k;
|
||||
|
||||
/* Flash region to be used for exception vectors.*/
|
||||
REGION_ALIAS("VECTORS_FLASH", flash0);
|
||||
REGION_ALIAS("VECTORS_FLASH_LMA", flash0);
|
||||
|
||||
/* Flash region to be used for constructors and destructors.*/
|
||||
REGION_ALIAS("XTORS_FLASH", flash2);
|
||||
REGION_ALIAS("XTORS_FLASH_LMA", flash2);
|
||||
|
||||
/* Flash region to be used for code text.*/
|
||||
REGION_ALIAS("TEXT_FLASH", flash2);
|
||||
REGION_ALIAS("TEXT_FLASH_LMA", flash2);
|
||||
|
||||
/* Flash region to be used for read only data.*/
|
||||
REGION_ALIAS("RODATA_FLASH", flash2);
|
||||
REGION_ALIAS("RODATA_FLASH_LMA", flash2);
|
||||
|
||||
/* Flash region to be used for various.*/
|
||||
REGION_ALIAS("VARIOUS_FLASH", flash2);
|
||||
REGION_ALIAS("VARIOUS_FLASH_LMA", flash2);
|
||||
|
||||
/* Flash region to be used for RAM(n) initialization data.*/
|
||||
REGION_ALIAS("RAM_INIT_FLASH_LMA", flash2);
|
||||
|
||||
/* RAM region to be used for Main stack. This stack accommodates the processing
|
||||
of all exceptions and interrupts.*/
|
||||
REGION_ALIAS("MAIN_STACK_RAM", ram0);
|
||||
|
||||
/* RAM region to be used for the process stack. This is the stack used by
|
||||
the main() function.*/
|
||||
REGION_ALIAS("PROCESS_STACK_RAM", ram0);
|
||||
|
||||
/* RAM region to be used for data segment.*/
|
||||
REGION_ALIAS("DATA_RAM", ram0);
|
||||
REGION_ALIAS("DATA_RAM_LMA", flash2);
|
||||
|
||||
/* RAM region to be used for BSS segment.*/
|
||||
REGION_ALIAS("BSS_RAM", ram0);
|
||||
|
||||
/* RAM region to be used for the default heap.*/
|
||||
REGION_ALIAS("HEAP_RAM", ram0);
|
||||
|
||||
/* Generic rules inclusion.*/
|
||||
INCLUDE rules.ld
|
||||
INCLUDE stm32f4xx_common.ld
|
||||
|
@ -1,89 +1,13 @@
|
||||
/*
|
||||
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* STM32F405xG memory setup.
|
||||
* Note: Use of ram1 and ram2 is mutually exclusive with use of ram0.
|
||||
* Copyright 2006..2018 Giovanni Di Sirio
|
||||
* Copyright 2022 QMK contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
MEMORY
|
||||
{
|
||||
flash0 (rx) : org = 0x08000000 + 64k, len = 1M - 64k /* tinyuf2 bootloader requires app to be located at 64k offset for this MCU */
|
||||
flash1 (rx) : org = 0x00000000, len = 0
|
||||
flash2 (rx) : org = 0x00000000, len = 0
|
||||
flash3 (rx) : org = 0x00000000, len = 0
|
||||
flash4 (rx) : org = 0x00000000, len = 0
|
||||
flash5 (rx) : org = 0x00000000, len = 0
|
||||
flash6 (rx) : org = 0x00000000, len = 0
|
||||
flash7 (rx) : org = 0x00000000, len = 0
|
||||
ram0 (wx) : org = 0x20000000, len = 128k /* SRAM1 + SRAM2 */
|
||||
ram1 (wx) : org = 0x20000000, len = 112k /* SRAM1 */
|
||||
ram2 (wx) : org = 0x2001C000, len = 16k /* SRAM2 */
|
||||
ram3 (wx) : org = 0x00000000, len = 0
|
||||
ram4 (wx) : org = 0x10000000, len = 64k /* CCM SRAM */
|
||||
ram5 (wx) : org = 0x40024000, len = 4k /* BCKP SRAM */
|
||||
ram6 (wx) : org = 0x00000000, len = 0
|
||||
ram7 (wx) : org = 0x00000000, len = 0
|
||||
}
|
||||
|
||||
/* For each data/text section two region are defined, a virtual region
|
||||
and a load region (_LMA suffix).*/
|
||||
f4xx_flash_size = 1M;
|
||||
f4xx_ram1_size = 112k;
|
||||
f4xx_ram2_size = 16k;
|
||||
f4xx_ram4_size = 64k;
|
||||
f4xx_ram5_size = 4k;
|
||||
|
||||
/* Flash region to be used for exception vectors.*/
|
||||
REGION_ALIAS("VECTORS_FLASH", flash0);
|
||||
REGION_ALIAS("VECTORS_FLASH_LMA", flash0);
|
||||
|
||||
/* Flash region to be used for constructors and destructors.*/
|
||||
REGION_ALIAS("XTORS_FLASH", flash0);
|
||||
REGION_ALIAS("XTORS_FLASH_LMA", flash0);
|
||||
|
||||
/* Flash region to be used for code text.*/
|
||||
REGION_ALIAS("TEXT_FLASH", flash0);
|
||||
REGION_ALIAS("TEXT_FLASH_LMA", flash0);
|
||||
|
||||
/* Flash region to be used for read only data.*/
|
||||
REGION_ALIAS("RODATA_FLASH", flash0);
|
||||
REGION_ALIAS("RODATA_FLASH_LMA", flash0);
|
||||
|
||||
/* Flash region to be used for various.*/
|
||||
REGION_ALIAS("VARIOUS_FLASH", flash0);
|
||||
REGION_ALIAS("VARIOUS_FLASH_LMA", flash0);
|
||||
|
||||
/* Flash region to be used for RAM(n) initialization data.*/
|
||||
REGION_ALIAS("RAM_INIT_FLASH_LMA", flash0);
|
||||
|
||||
/* RAM region to be used for Main stack. This stack accommodates the processing
|
||||
of all exceptions and interrupts.*/
|
||||
REGION_ALIAS("MAIN_STACK_RAM", ram0);
|
||||
|
||||
/* RAM region to be used for the process stack. This is the stack used by
|
||||
the main() function.*/
|
||||
REGION_ALIAS("PROCESS_STACK_RAM", ram0);
|
||||
|
||||
/* RAM region to be used for data segment.*/
|
||||
REGION_ALIAS("DATA_RAM", ram0);
|
||||
REGION_ALIAS("DATA_RAM_LMA", flash0);
|
||||
|
||||
/* RAM region to be used for BSS segment.*/
|
||||
REGION_ALIAS("BSS_RAM", ram0);
|
||||
|
||||
/* RAM region to be used for the default heap.*/
|
||||
REGION_ALIAS("HEAP_RAM", ram0);
|
||||
|
||||
/* Generic rules inclusion.*/
|
||||
INCLUDE rules.ld
|
||||
|
||||
/* TinyUF2 bootloader reset support */
|
||||
_board_dfu_dbl_tap = ORIGIN(ram0) + 64k - 4; /* this is based off the linker file for tinyuf2 */
|
||||
INCLUDE stm32f4xx_tinyuf2_common.ld
|
||||
|
13
platforms/chibios/boards/common/ld/STM32F407xE.ld
Normal file
13
platforms/chibios/boards/common/ld/STM32F407xE.ld
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2006..2018 Giovanni Di Sirio
|
||||
* Copyright 2022 QMK contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
f4xx_flash_size = 512k;
|
||||
f4xx_ram1_size = 112k;
|
||||
f4xx_ram2_size = 16k;
|
||||
f4xx_ram4_size = 64k;
|
||||
f4xx_ram5_size = 4k;
|
||||
|
||||
INCLUDE stm32f4xx_common.ld
|
13
platforms/chibios/boards/common/ld/STM32F407xE_tinyuf2.ld
Normal file
13
platforms/chibios/boards/common/ld/STM32F407xE_tinyuf2.ld
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2006..2018 Giovanni Di Sirio
|
||||
* Copyright 2022 QMK contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
f4xx_flash_size = 512k;
|
||||
f4xx_ram1_size = 112k;
|
||||
f4xx_ram2_size = 16k;
|
||||
f4xx_ram4_size = 64k;
|
||||
f4xx_ram5_size = 4k;
|
||||
|
||||
INCLUDE stm32f4xx_tinyuf2_common.ld
|
@ -5,6 +5,9 @@
|
||||
*/
|
||||
|
||||
f4xx_flash_size = 256k;
|
||||
f4xx_ram_size = 128k;
|
||||
f4xx_ram1_size = 128k;
|
||||
f4xx_ram2_size = 0;
|
||||
f4xx_ram4_size = 0;
|
||||
f4xx_ram5_size = 0;
|
||||
|
||||
INCLUDE stm32f4xx_common.ld
|
||||
|
@ -5,6 +5,9 @@
|
||||
*/
|
||||
|
||||
f4xx_flash_size = 256k;
|
||||
f4xx_ram_size = 128k;
|
||||
f4xx_ram1_size = 128k;
|
||||
f4xx_ram2_size = 0;
|
||||
f4xx_ram4_size = 0;
|
||||
f4xx_ram5_size = 0;
|
||||
|
||||
INCLUDE stm32f4xx_tinyuf2_common.ld
|
||||
|
@ -5,6 +5,9 @@
|
||||
*/
|
||||
|
||||
f4xx_flash_size = 512k;
|
||||
f4xx_ram_size = 128k;
|
||||
f4xx_ram1_size = 128k;
|
||||
f4xx_ram2_size = 0;
|
||||
f4xx_ram4_size = 0;
|
||||
f4xx_ram5_size = 0;
|
||||
|
||||
INCLUDE stm32f4xx_common.ld
|
||||
|
@ -5,6 +5,9 @@
|
||||
*/
|
||||
|
||||
f4xx_flash_size = 512k;
|
||||
f4xx_ram_size = 128k;
|
||||
f4xx_ram1_size = 128k;
|
||||
f4xx_ram2_size = 0;
|
||||
f4xx_ram4_size = 0;
|
||||
f4xx_ram5_size = 0;
|
||||
|
||||
INCLUDE stm32f4xx_tinyuf2_common.ld
|
||||
|
13
platforms/chibios/boards/common/ld/STM32F446xE.ld
Normal file
13
platforms/chibios/boards/common/ld/STM32F446xE.ld
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2006..2018 Giovanni Di Sirio
|
||||
* Copyright 2022 QMK contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
f4xx_flash_size = 512k;
|
||||
f4xx_ram1_size = 112k;
|
||||
f4xx_ram2_size = 16k;
|
||||
f4xx_ram4_size = 0;
|
||||
f4xx_ram5_size = 4k;
|
||||
|
||||
INCLUDE stm32f4xx_common.ld
|
13
platforms/chibios/boards/common/ld/STM32F446xE_tinyuf2.ld
Normal file
13
platforms/chibios/boards/common/ld/STM32F446xE_tinyuf2.ld
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2006..2018 Giovanni Di Sirio
|
||||
* Copyright 2022 QMK contributors
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
f4xx_flash_size = 512k;
|
||||
f4xx_ram1_size = 112k;
|
||||
f4xx_ram2_size = 16k;
|
||||
f4xx_ram4_size = 0;
|
||||
f4xx_ram5_size = 4k;
|
||||
|
||||
INCLUDE stm32f4xx_tinyuf2_common.ld
|
@ -24,12 +24,12 @@ MEMORY
|
||||
flash5 (rx) : org = 0x00000000, len = 0
|
||||
flash6 (rx) : org = 0x00000000, len = 0
|
||||
flash7 (rx) : org = 0x00000000, len = 0
|
||||
ram0 (wx) : org = 0x20000000, len = f4xx_ram_size
|
||||
ram1 (wx) : org = 0x00000000, len = 0
|
||||
ram2 (wx) : org = 0x00000000, len = 0
|
||||
ram0 (wx) : org = 0x20000000, len = f4xx_ram1_size + f4xx_ram2_size /* SRAM1 + SRAM2 */
|
||||
ram1 (wx) : org = 0x20000000, len = f4xx_ram1_size /* SRAM1 */
|
||||
ram2 (wx) : org = 0x20000000 + f4xx_ram1_size, len = f4xx_ram2_size /* SRAM2 */
|
||||
ram3 (wx) : org = 0x00000000, len = 0
|
||||
ram4 (wx) : org = 0x00000000, len = 0
|
||||
ram5 (wx) : org = 0x00000000, len = 0
|
||||
ram4 (wx) : org = 0x10000000, len = f4xx_ram4_size /* CCM SRAM */
|
||||
ram5 (wx) : org = 0x40024000, len = f4xx_ram5_size /* BCKP SRAM */
|
||||
ram6 (wx) : org = 0x00000000, len = 0
|
||||
ram7 (wx) : org = 0x00000000, len = 0
|
||||
}
|
||||
@ -80,4 +80,3 @@ REGION_ALIAS("HEAP_RAM", ram0);
|
||||
|
||||
/* Generic rules inclusion.*/
|
||||
INCLUDE rules.ld
|
||||
|
||||
|
@ -27,12 +27,12 @@ MEMORY
|
||||
flash5 (rx) : org = 0x00000000, len = 0
|
||||
flash6 (rx) : org = 0x00000000, len = 0
|
||||
flash7 (rx) : org = 0x00000000, len = 0
|
||||
ram0 (wx) : org = 0x20000000, len = f4xx_ram_size
|
||||
ram1 (wx) : org = 0x00000000, len = 0
|
||||
ram2 (wx) : org = 0x00000000, len = 0
|
||||
ram0 (wx) : org = 0x20000000, len = f4xx_ram1_size + f4xx_ram2_size /* SRAM1 + SRAM2 */
|
||||
ram1 (wx) : org = 0x20000000, len = f4xx_ram1_size /* SRAM1 */
|
||||
ram2 (wx) : org = 0x20000000 + f4xx_ram1_size, len = f4xx_ram2_size /* SRAM2 */
|
||||
ram3 (wx) : org = 0x00000000, len = 0
|
||||
ram4 (wx) : org = 0x00000000, len = 0
|
||||
ram5 (wx) : org = 0x00000000, len = 0
|
||||
ram4 (wx) : org = 0x10000000, len = f4xx_ram4_size /* CCM SRAM */
|
||||
ram5 (wx) : org = 0x40024000, len = f4xx_ram5_size /* BCKP SRAM */
|
||||
ram6 (wx) : org = 0x00000000, len = 0
|
||||
ram7 (wx) : org = 0x00000000, len = 0
|
||||
}
|
||||
@ -86,5 +86,3 @@ INCLUDE rules.ld
|
||||
|
||||
/* TinyUF2 bootloader reset support */
|
||||
_board_dfu_dbl_tap = ORIGIN(ram0) + 64k - 4; /* this is based off the linker file for tinyuf2 */
|
||||
|
||||
|
||||
|
@ -140,9 +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
|
||||
#ifdef LAYER_LOCK_ENABLE
|
||||
# include "layer_lock.h"
|
||||
#endif // LAYER_LOCK_ENABLE
|
||||
#endif
|
||||
|
||||
static uint32_t last_input_modification_time = 0;
|
||||
uint32_t last_input_activity_time(void) {
|
||||
@ -659,7 +659,7 @@ void quantum_task(void) {
|
||||
secure_task();
|
||||
#endif
|
||||
|
||||
#if defined(LAYER_LOCK_ENABLE) && LAYER_LOCK_IDLE_TIMEOUT > 0
|
||||
#ifdef LAYER_LOCK_ENABLE
|
||||
layer_lock_task();
|
||||
#endif
|
||||
}
|
||||
|
@ -23,12 +23,18 @@ layer_state_t locked_layers = 0;
|
||||
# if defined(LAYER_LOCK_IDLE_TIMEOUT) && LAYER_LOCK_IDLE_TIMEOUT > 0
|
||||
uint32_t layer_lock_timer = 0;
|
||||
|
||||
void layer_lock_task(void) {
|
||||
void layer_lock_timeout_task(void) {
|
||||
if (locked_layers && timer_elapsed32(layer_lock_timer) > LAYER_LOCK_IDLE_TIMEOUT) {
|
||||
layer_lock_all_off();
|
||||
layer_lock_timer = timer_read32();
|
||||
}
|
||||
}
|
||||
void layer_lock_activity_trigger(void) {
|
||||
layer_lock_timer = timer_read32();
|
||||
}
|
||||
# else
|
||||
void layer_lock_timeout_task(void) {}
|
||||
void layer_lock_activity_trigger(void) {}
|
||||
# endif // LAYER_LOCK_IDLE_TIMEOUT > 0
|
||||
|
||||
bool is_layer_locked(uint8_t layer) {
|
||||
@ -44,9 +50,7 @@ void layer_lock_invert(uint8_t layer) {
|
||||
}
|
||||
# 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
|
||||
layer_lock_activity_trigger();
|
||||
} else { // Layer is being unlocked.
|
||||
layer_off(layer);
|
||||
}
|
||||
@ -72,10 +76,25 @@ void layer_lock_all_off(void) {
|
||||
layer_lock_set_kb(locked_layers);
|
||||
}
|
||||
|
||||
#else // NO_ACTION_LAYER
|
||||
bool is_layer_locked(uint8_t layer) {
|
||||
return false;
|
||||
}
|
||||
void layer_lock_on(uint8_t layer) {}
|
||||
void layer_lock_off(uint8_t layer) {}
|
||||
void layer_lock_all_off(void) {}
|
||||
void layer_lock_invert(uint8_t layer) {}
|
||||
void layer_lock_timeout_task(void) {}
|
||||
void layer_lock_activity_trigger(void) {}
|
||||
#endif // NO_ACTION_LAYER
|
||||
|
||||
__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
|
||||
|
||||
void layer_lock_task(void) {
|
||||
layer_lock_timeout_task();
|
||||
}
|
||||
|
@ -49,13 +49,6 @@
|
||||
*
|
||||
* #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>
|
||||
*/
|
||||
@ -67,24 +60,6 @@
|
||||
#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);
|
||||
|
||||
@ -116,20 +91,8 @@ void layer_lock_invert(uint8_t layer);
|
||||
bool layer_lock_set_kb(layer_state_t locked_layers);
|
||||
bool layer_lock_set_user(layer_state_t locked_layers);
|
||||
|
||||
/** Handle various background tasks */
|
||||
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
|
||||
|
||||
/** Update any configured timeouts */
|
||||
void layer_lock_activity_trigger(void);
|
||||
|
@ -12,14 +12,6 @@
|
||||
// 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"
|
||||
@ -27,12 +19,9 @@
|
||||
|
||||
// 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) {
|
||||
static inline 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);
|
||||
@ -44,9 +33,7 @@ static bool handle_mo_or_tt(uint8_t layer, keyrecord_t* record) {
|
||||
|
||||
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
|
||||
layer_lock_activity_trigger();
|
||||
|
||||
// The intention is that locked layers remain on. If something outside of
|
||||
// this feature turned any locked layers off, unlock them.
|
||||
|
@ -12,54 +12,6 @@
|
||||
// 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>
|
||||
|
@ -78,7 +78,7 @@
|
||||
|
||||
#ifdef LAYER_LOCK_ENABLE
|
||||
# include "process_layer_lock.h"
|
||||
#endif // LAYER_LOCK_ENABLE
|
||||
#endif
|
||||
|
||||
#ifdef AUDIO_ENABLE
|
||||
# ifndef GOODBYE_SONG
|
||||
|
@ -242,7 +242,7 @@ extern layer_state_t layer_state;
|
||||
|
||||
#ifdef LAYER_LOCK_ENABLE
|
||||
# include "layer_lock.h"
|
||||
#endif // LAYER_LOCK_ENABLE
|
||||
#endif
|
||||
|
||||
void set_single_default_layer(uint8_t default_layer);
|
||||
void set_single_persistent_default_layer(uint8_t default_layer);
|
||||
|
@ -2,35 +2,42 @@
|
||||
RGB_MATRIX_EFFECT(RAINDROPS)
|
||||
# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
|
||||
|
||||
static void raindrops_set_color(int i, effect_params_t* params) {
|
||||
static void raindrops_set_color(uint8_t i, effect_params_t* params) {
|
||||
if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) return;
|
||||
hsv_t hsv = {0, rgb_matrix_config.hsv.s, rgb_matrix_config.hsv.v};
|
||||
hsv_t hsv = rgb_matrix_config.hsv;
|
||||
|
||||
// Take the shortest path between hues
|
||||
int16_t deltaH = ((rgb_matrix_config.hsv.h + 180) % 360 - rgb_matrix_config.hsv.h) / 4;
|
||||
int16_t deltaH = ((hsv.h + 180) % 360 - hsv.h) / 4;
|
||||
if (deltaH > 127) {
|
||||
deltaH -= 256;
|
||||
} else if (deltaH < -127) {
|
||||
deltaH += 256;
|
||||
}
|
||||
|
||||
hsv.h = rgb_matrix_config.hsv.h + (deltaH * (random8() & 0x03));
|
||||
hsv.h += (deltaH * random8_max(3));
|
||||
rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv);
|
||||
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
|
||||
}
|
||||
|
||||
bool RAINDROPS(effect_params_t* params) {
|
||||
static uint16_t index = RGB_MATRIX_LED_COUNT + 1;
|
||||
|
||||
// Periodic trigger for LED change
|
||||
if ((params->iter == 0) && (scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 16)) % 10 == 0)) {
|
||||
index = random8_max(RGB_MATRIX_LED_COUNT);
|
||||
}
|
||||
|
||||
RGB_MATRIX_USE_LIMITS(led_min, led_max);
|
||||
if (!params->init) {
|
||||
// Change one LED every tick, make sure speed is not 0
|
||||
if (scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 16)) % 10 == 0) {
|
||||
raindrops_set_color(random8_max(RGB_MATRIX_LED_COUNT), params);
|
||||
}
|
||||
} else {
|
||||
for (int i = led_min; i < led_max; i++) {
|
||||
if (params->init) {
|
||||
for (uint8_t i = led_min; i < led_max; i++) {
|
||||
raindrops_set_color(i, params);
|
||||
}
|
||||
}
|
||||
// Change LED once and set index out of range till next trigger
|
||||
else if (led_min <= index && index < led_max) {
|
||||
raindrops_set_color(index, params);
|
||||
index = RGB_MATRIX_LED_COUNT + 1;
|
||||
}
|
||||
return rgb_matrix_check_finished_leds(led_max);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user