aboutsummaryrefslogtreecommitdiff
path: root/docs/zh-cn/custom_quantum_functions.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/zh-cn/custom_quantum_functions.md')
-rw-r--r--docs/zh-cn/custom_quantum_functions.md367
1 files changed, 177 insertions, 190 deletions
diff --git a/docs/zh-cn/custom_quantum_functions.md b/docs/zh-cn/custom_quantum_functions.md
index 1ae996e39..29c508905 100644
--- a/docs/zh-cn/custom_quantum_functions.md
+++ b/docs/zh-cn/custom_quantum_functions.md
@@ -1,31 +1,35 @@
1# 如何定制键盘功能 1# 如何定制键盘功能
2 2
3对于很多人来说客制化键盘可不只是向你的电脑发送你按了那个件这么简单。你肯定想实现比简单按键和宏更复杂的功能。QMK有能让你注入代码的钩子, 覆盖功能, 另外,还可以自定义键盘在不同情况下的行为。 3<!---
4 original document: 0.15.12:docs/custom_quantum_functions.md
5 git diff 0.15.12 HEAD -- docs/custom_quantum_functions.md | cat
6-->
4 7
5本页不假定任何特殊的QMK知识,但阅读[理解QMK](understanding_qmk.md)将会在更基础的层面帮你理解发生了什么。 8对于很多人来说对客制化键盘的诉求不只是向电脑输入按下的键。你肯定想实现比简单按键和宏更复杂的功能。QMK支持基于注入点的代码注入,功能重写,另外还可以自定义键盘在不同情况下的行为。
6 9
7## A Word on Core vs 键盘 vs 布局 10本页不要求任何额外的QMK知识基础,但阅读[理解QMK](zh-cn/understanding_qmk.md)将会在更基础的层面帮你理解发生了什么。
8 11
9我们把qmk组织成一个层次结构: 12## 核心/键盘/键映射的概念 :id=a-word-on-core-vs-keyboards-vs-keymap
13
14QMK基于如下层级组成:
10 15
11* Core (`_quantum`) 16* Core (`_quantum`)
12 * Keyboard/Revision (`_kb`) 17 * Keyboard/Revision (`_kb`)
13 * Keymap (`_user`) 18 * Keymap (`_user`)
14 19
15下面描述的每一个函数都可以在定义上加一个`_kb()`或 `_user()` 后缀。 建议在键盘/修订层使用`_kb()`后缀,在布局层使用`_user()`后缀。 20该文后续部分所提及的函数在定义时皆可添加 `_kb()` 或 `_user()` 后缀,我们建议在键盘及其子版本中使用 `_kb()` 后缀,而在键映射中使用 `_user()` 后缀。
16 21
17在键盘/修订层定义函数时,`_kb()`在执行任何代码前先调用`_user()`是必要的,不然布局层函数就不要被调用。 22在键盘及其子版本中定义函数时,一个重要的点是在 `_kb()` 函数执行任何逻辑前,应先调用 `_user()` 函数,否则这些键映射中的函数将没有机会被执行。
18<!-- 翻译问题:上面那句翻译的不太好-->
19# 自定义键码 23# 自定义键码
20 24
21到目前为止,最常见的任务是更改现有键码的行为或创建新的键码。从代码角度来看这些操作都很相似。 25到目前为止,最常见的任务是更改现有键码的行为或创建新的键码。从代码角度来看这些操作都很相似。
22 26
23## 定义一个新键码 27## 定义一个新键码
24 28
25创建键码第一步,先枚举出它全部,也就是给键码起个名字并分配唯一数值。QMK没有直接限制最大键码值大小,而是提供了一个`SAFE_RANGE`宏。你可以在枚举时用`SAFE_RANGE`来保证你取得了唯一的键码值。 29创建键码的第一步,是先定义其枚举值,也就是给键码起个名字并分配一个唯一值。QMK没有直接限制最大可用的键码值,而是提供了一个 `SAFE_RANGE` 宏。你可以在定义枚举时用 `SAFE_RANGE` 来保证你取得了唯一的键码值。
26 30
27 31
28这有枚举两个键码的例子。把这块加到`keymap.c`的话你就在布局中能用`FOO`和`BAR`了。 32这有定义两个键码的枚举值的例子。添加以下代码块至 `keymap.c` 后你就可以在布局中使用 `FOO` 和 `BAR` 了。
29 33
30```c 34```c
31enum my_keycodes { 35enum my_keycodes {
@@ -34,15 +38,15 @@ enum my_keycodes {
34}; 38};
35``` 39```
36 40
37## 键码的行为编程 41## 编程设计键码的行为 :id=programming-the-behavior-of-any-keycode
38 42
39当你覆盖一个已存在按键的行为时,或将这个行为赋给新键时,你要用`process_record_kb()`和`process_record_user()`函数。这俩函数在键处理中真实键事件被处理前被QMK调用。如果这俩函数返回`true`,QMK将会用正常的方式处理键码。这样可以很方便的扩展键码的功能而不是替换它。如果函数返回`false` QMK会跳过正常键处理,然后发送键子抬起还是按下事件就由你决定了。 43当你覆盖一个已存在按键的行为时,或是给新按键设计功能时,请使用 `process_record_kb()` 和 `process_record_user()` 函数。QMK会在响应并处理按键事件前调用这些函数,如果这些函数返回值为 `true`,QMK将继续用常规的方式处理键码,这样可以很方便的扩展键码的功能而不需要替换代码实现。如果函数返回`false` QMK会跳过常规的键处理逻辑,需要发送的按键按下或抬起事件则需交由你负责完成。
40 44
41当某键按下或这俩函用。 45意按按下或次都会调用这些函数
42 46
43### process_record_user()`示例实现 47### process_record_user()` 示例
44 48
45这个例子做了两个事。自定义了一个叫做`FOO`的键码的行为,并补充了在按下回车时播放音符。 49这个例子做了两个事。自定义了一个叫做 `FOO` 的键码的行为,并提供了在按下回车时播放音符的功能。
46 50
47```c 51```c
48bool process_record_user(uint16_t keycode, keyrecord_t *record) { 52bool process_record_user(uint16_t keycode, keyrecord_t *record) {
@@ -51,7 +55,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
51 if (record->event.pressed) { 55 if (record->event.pressed) {
52 // 按下时做些什么 56 // 按下时做些什么
53 } else { 57 } else {
54 // 时做些什么 58 // 时做些什么
55 } 59 }
56 return false; // 跳过此键的所有进一步处理 60 return false; // 跳过此键的所有进一步处理
57 case KC_ENTER: 61 case KC_ENTER:
@@ -59,21 +63,21 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
59 if (record->event.pressed) { 63 if (record->event.pressed) {
60 PLAY_SONG(tone_qwerty); 64 PLAY_SONG(tone_qwerty);
61 } 65 }
62 return true; // 让QMK回车按下/事件 66 return true; // 让QMK车按下/事件
63 default: 67 default:
64 return true; // 正常其他键码 68 return true; // 正常他键码
65 } 69 }
66} 70}
67``` 71```
68 72
69### `process_record_*` 文档 73### `process_record_*` 示例
70 74
71* 键盘/修订: `bool process_record_kb(uint16_t keycode, keyrecord_t *record)` 75* 键盘/各子版本:`bool process_record_kb(uint16_t keycode, keyrecord_t *record)`
72* 局: `bool process_record_user(uint16_t keycode, keyrecord_t *record)` 76* 键映`bool process_record_user(uint16_t keycode, keyrecord_t *record)`
73 77
74`keycode(键码)`参数是在布局上定义的,比如`MO(1)`, `KC_L`, 等等。 你要用 `switch...case` 块来处理这些事件。 78`keycode` 参数为键映射中形如 `MO(1)`,`KC_L` 等定义的键值项。 应使用 `switch...case` 代码块来处理这些事件。
75 79
76`record`参数含有实际按键的信息: 80`record` 参数含有按键的真实状态信息:
77 81
78```c 82```c
79keyrecord_t record { 83keyrecord_t record {
@@ -88,108 +92,31 @@ keyrecord_t record {
88} 92}
89``` 93```
90 94
91# LED控制
92
93qmk提供了读取HID规范包含的5个LED的方法。:
94
95* `USB_LED_NUM_LOCK`
96* `USB_LED_CAPS_LOCK`
97* `USB_LED_SCROLL_LOCK`
98* `USB_LED_COMPOSE`
99* `USB_LED_KANA`
100
101这五个常量对应于主机LED状态的位置位。
102有两种方法可以获得主机LED状态:
103
104* 通过执行 `led_set_user()`
105* 通过调用 `host_keyboard_leds()`
106
107## `led_set_user()`
108
109当5个LED中任何一个的状态需要改变时,此函数将被调用。此函数通过参数输入LED参数。
110使用`IS_LED_ON(usb_led, led_name)`和`IS_LED_OFF(usb_led, led_name)`这两个宏来检查LED状态。
111
112!> `host_keyboard_leds()`可能会在`led_set_user()`被调用前返回新值。
113
114### `led_set_user()`函数示例实现
115
116```c
117void led_set_user(uint8_t usb_led) {
118 if (IS_LED_ON(usb_led, USB_LED_NUM_LOCK)) {
119 writePinLow(B0);
120 } else {
121 writePinHigh(B0);
122 }
123 if (IS_LED_ON(usb_led, USB_LED_CAPS_LOCK)) {
124 writePinLow(B1);
125 } else {
126 writePinHigh(B1);
127 }
128 if (IS_LED_ON(usb_led, USB_LED_SCROLL_LOCK)) {
129 writePinLow(B2);
130 } else {
131 writePinHigh(B2);
132 }
133 if (IS_LED_ON(usb_led, USB_LED_COMPOSE)) {
134 writePinLow(B3);
135 } else {
136 writePinHigh(B3);
137 }
138 if (IS_LED_ON(usb_led, USB_LED_KANA)) {
139 writePinLow(B4);
140 } else {
141 writePinHigh(B4);
142 }
143}
144```
145
146### `led_set_*`函数文档
147
148* 键盘/修订: `void led_set_kb(uint8_t usb_led)`
149* 布局: `void led_set_user(uint8_t usb_led)`
150
151## `host_keyboard_leds()`
152
153调用这个函数会返回最后收到的LED状态。这个函数在`led_set_*`之外读取LED状态时很有用,比如在[`matrix_scan_user()`](#矩阵扫描代码).
154为了便捷,你可以用`IS_HOST_LED_ON(led_name)`和`IS_HOST_LED_OFF(led_name)` 宏,而不直接调用和检查`host_keyboard_leds()`。
155
156## 设置物理LED状态
157
158一些键盘实现了为设置物理LED的状态提供了方便的方法。
159
160### Ergodox Boards
161
162Ergodox实现了提供`ergodox_right_led_1`/`2`/`3_on`/`off()`来让每个LED开或关, 也可以用 `ergodox_right_led_on`/`off(uint8_t led)` 按索引打开或关闭他们。
163
164此外,还可以使用`ergodox_led_all_set(uint8_t n)`指定所有LED的亮度级别;针对每个LED用`ergodox_right_led_1`/`2`/`3_set(uint8_t n)`;使用索引的话用`ergodox_right_led_set(uint8_t led, uint8_t n)`。
165
166Ergodox boards 同时定义了最低亮度级别`LED_BRIGHTNESS_LO`和最高亮度级别`LED_BRIGHTNESS_HI`(默认最高).
167
168# 键盘初始化代码 95# 键盘初始化代码
169 96
170键盘初始化过程几个步骤那个数取 97键盘初始化过程须经过几个步骤目的注哪函数
171 98
172有三个主要初始化函数,按调用顺序列出。 99有三个主要初始化函数,按调用顺序列出。
173 100
174* `keyboard_pre_init_*` - 会在大多数其他东西运行前运行。适用于哪些需要提前运行的硬件初始化。 101* `keyboard_pre_init_*` - 会在大多数其他功能运行前执行。适用于那些需要尽早执行的硬件初始化工作。
175* `matrix_init_*` - 在固件启动过程中被调用。此时硬件已初始化,功能未初始化 102* `matrix_init_*` - 在固件启动过程中被调用。此时硬件已初始化,但部还不
176* `keyboard_post_init_*` - 在固件启动过程最后被调用。大多数情况下,你的“客制化”代码都可以放在这里。 103* `keyboard_post_init_*` - 在固件启动过程的最后被调用。大多数情况下,你的“客制化”代码都可以放在这里。
177 104
178!> 对于大多数人来说`keyboard_post_init_user`是你想要调用的函数。例如, 此时你可以设置RGB灯发光。 105!> 对于大多数人来说 `keyboard_post_init_user` 是你想要关注的函数。例如, 你可以在这里启动RGB背光灯。
179 106
180## 键盘预初始化代码 107## 键盘预初始化代码
181 108
182这代码运行,甚至在USB运行 109部分代码行的早,甚至在USB通信前。
183 110
184在这之后不久矩阵始化 111在这之后不久即会完成矩阵初始化。
185 112
186对于大多数用户来说,这用,因为它主要用于面向硬件初始化。 113对于大多数用户来说不在此处进行修改,因为它主要用于硬件初始化。
187 114
188但如果你有硬件初始化的话放在这里再好不过了(比如初始化LED引脚一类的). 115但如果你有硬件初始化的话放在这里再好不过了比如初始化LED引脚.
189 116
190### `keyboard_pre_init_user()`示例实现 117### `keyboard_pre_init_user()` 示例
191 118
192本例中在键盘,设 B0, B1, B2, B3, 和 B4 LED引脚。 119本例中在键盘 B0, B1, B2, B3, 和 B4 引脚设置为LED引脚。
193 120
194```c 121```c
195void keyboard_pre_init_user(void) { 122void keyboard_pre_init_user(void) {
@@ -206,95 +133,110 @@ void keyboard_pre_init_user(void) {
206 133
207### `keyboard_pre_init_*` 函数文档 134### `keyboard_pre_init_*` 函数文档
208 135
209* 键盘/修订: `void keyboard_pre_init_kb(void)` 136* 键盘/各子版本:`void keyboard_pre_init_kb(void)`
210* 局: `void keyboard_pre_init_user(void)` 137* 键映`void keyboard_pre_init_user(void)`
211 138
212## 矩阵初始化代码 139## 矩阵初始化代码
213 140
214矩阵初始化被调用,在硬件设置,但一些功能初始化 141在矩阵初始化被调用部分硬件设置,但一些功能未完始化。
215 142
216你设置地方会西的时很有用,硬件无关, 143来设与硬件无关,没有殊要求
217 144
218 145
219### `matrix_init_*`函数文档 146### `matrix_init_*` 函数文档
220 147
221* 键盘/修订: `void matrix_init_kb(void)` 148* 键盘/各子版本:`void matrix_init_kb(void)`
222* 局: `void matrix_init_user(void)` 149* 键映`void matrix_init_user(void)`
223 150
151### 低级矩阵函数的重写 :id=low-level-matrix-overrides
224 152
225## 键盘后初始化代码 153* GPIO引脚初始化:`void matrix_init_pins(void)`
154 * 此处须完成低级行列引脚的初始化。默认实现中,这里会参考可选的键盘设置项 `ROW2COL`,`COL2ROW` 及 `DIRECT_PINS` 来初始化所有 `MATRIX_ROW_PINS` 及 `MATRIX_COL_PINS` 中定义的GPIO引脚的输入/输出状态。当键盘设计者重写该函数后,QMK本身不会进行任何引脚的初始化,只会听从重写的函数的实现逻辑。
155* `COL2ROW`-从行中读: `void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)`
156* `ROW2COL`-从列中读: `void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col)`
157* `DIRECT_PINS`-直读: `void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)`
158 * 以上三个函数须参考矩阵类别,从底层矩阵的相关引脚状态中获取输入信息,并且应该只需要实现三者之一。默认情况下,在遍历 `MATRIX_ROW_PINS` and `MATRIX_COL_PINS` 时,会根据是否设置了 `ROW2COL`,`COL2ROW` 或 `DIRECT_PINS` 来配置输入输出方式。当键盘设计者重写该函数后,QMK本身不会进行任何矩阵GPIO引脚状态的变更,只会听从重写的函数的实现逻辑。
226 159
227这是键盘初始化过程中的最后一个任务。如果您想更改某些特性,这会很有用,因为此时应该对它们进行初始化。 160## 键盘后初始化代码
228 161
162这是键盘初始化过程中的最后一个任务。此时您可以配置并调整某些特性,因为此时这些特性已初始化完毕。
229 163
230### `keyboard_post_init_user()`示例实现 164### `keyboard_post_init_user()` 实现示例
231 165
232本示例在所有初始化完成后运行,配置RGB 166本示例在所有初始化完成后运行,配置RGB背光
233 167
234```c 168```c
235void keyboard_post_init_user(void) { 169void keyboard_post_init_user(void) {
236 // 调用后初始化代码 170 // 调用后初始化代码
237 rgblight_enable_noeeprom(); // 使能Rgb,不保存设置 171 rgblight_enable_noeeprom(); // 使能Rgb,不保存设置
238 rgblight_sethsv_noeeprom(180, 255, 255); // 将颜色设置到蓝绿色(青色)不保存 172 rgblight_sethsv_noeeprom(180, 255, 255); // 将颜色设置到蓝绿色(青色)不保存设置
239 rgblight_mode_noeeprom(RGBLIGHT_MODE_BREATHING + 3); // 设置快速呼吸模式不保存 173 rgblight_mode_noeeprom(RGBLIGHT_MODE_BREATHING + 3); // 设置快速呼吸模式不保存设置
240} 174}
241``` 175```
242 176
243### `keyboard_post_init_*` 函数文档 177### `keyboard_post_init_*` 函数文档
244 178
245* 键盘/修订: `void keyboard_post_init_kb(void)` 179* 键盘/各子版本:`void keyboard_post_init_kb(void)`
246* 布局: `void keyboard_post_init_user(void)` 180* 布局: `void keyboard_post_init_user(void)`
247 181
248# 矩阵扫描 182# 矩阵扫描码
249 183
250可能的话你要用`process_record_*()`自定义键盘,以这种方式连接到事件中,以确保代码不会对键盘产生负面的性能影响。然而,在极少数情况下,有必要进行矩阵扫描。在这些函数中要特别注意代码的性能,因为它每秒至少被调用10次。 184应尽量使用 `process_record_*()` 实现所需的键盘自定义以及事件监听,以确保这些代码不会对键盘性能产生负面的影响。然而,在极少数情况下需要在矩阵扫描中添加监听,此时需要极端留意这些函数代码的性能表现,因为这些函数每秒可能被执行十数次。
251 185
252### `matrix_scan_*`示例实现 186### `matrix_scan_*` 实现示例
253 187
254这个例子被故意省略了。在hook这样一个对性能及其敏感的区域之前,您应该足够了解qmk的内部结构,以便在没有示例的情况下编写。如果你需要帮助,请[建立一个issue](https://github.com/qmk/qmk_firmware/issues/new)或[在Discord上与我们交流](https://discord.gg/Uq7gcHh). 188这个例子被故意省略了。在监听处理这样一个对性能及其敏感的部分之前,您应该足够了解qmk的内部结构,才可以在没有示例的情况下编写。如果你需要帮助,请[新建一个issue](https://github.com/qmk/qmk_firmware/issues/new)或[在Discord上与我们交流](https://discord.gg/Uq7gcHh).
255 189
256### `matrix_scan_*` 函数文档 190### `matrix_scan_*` 函数文档
257 191
258* 键盘/修订: `void matrix_scan_kb(void)` 192* 键盘/各子版本:`void matrix_scan_kb(void)`
259* 布局: `void matrix_scan_user(void)` 193* 布局: `void matrix_scan_user(void)`
260 194
261该函数在每次矩阵扫描时被调用,这基本与MCU处理能力上限相同。在这里写代码要谨慎,因为它会运行很多次。 195该函数在每次矩阵扫描时被调用,这基本与MCU处理能力上限相同。在这里写代码要谨慎,因为它会运行很多次。
262 196
263你会在自定义矩阵扫描代码时用到这个函数。这也可以用作自定义状态输出(比如LED灯或者屏幕)或者其他即便用户不输入你也想定期运行的功能。 197在需要自定义矩阵扫描代码时可以使用该函数。这也可以用作自定义状态输出(比如LED灯或者屏幕)或者其他即便用户没有输入时你也想定期运行的功能。
198
199# Keyboard housekeeping
200
201* 键盘/各子版本:`void housekeeping_task_kb(void)`
202* 键映射:`void housekeeping_task_user(void)`
203
204该函数在所有QMK处理工作完毕后,下一轮开始执行前被执行。可以放心地假设此时QMK已对最新的矩阵扫描结果完成了所有的处理工作 -- 更新层状态,发送USB事件,更新LED状态,刷新显示屏。
264 205
206与 `matrix_scan_*` 类似,这些函数会频繁调用直至MCU处理能力上限。为了确保键盘的响应能力,建议在这些函数中尽量做最少的事情,在你确实需要在这里实现特别的功能时,可能会影响到其它功能的表现。
265 207
266# 键盘 空闲/唤醒 代码 208# 键盘 空闲/唤醒 代码
267 209
268如果键盘支持就可以通过停止一大票功能来达到"空闲"。RGB灯和背光就是很好的例子。这可以节约能耗,也可能让你键盘风味更佳。 210在主控板支持情况下,暂停大部分功能可以实现“空闲”状态,例如RGB灯光和背光。既可以节省电量消耗,也可能增强键盘的表现。
269 211
270两个函数控制: `suspend_power_down_*`和`suspend_wakeup_init_*`, 分别在板空闲和唤醒时调用。 212两个函数控制: `suspend_power_down_*` `suspend_wakeup_init_*`分别在空闲和唤醒时用。
271 213
272 214
273### suspend_power_down_user()和suspend_wakeup_init_user()实现 215### suspend_power_down_user() suspend_wakeup_init_user() 实现示例
274 216
275 217
276```c 218```c
277void suspend_power_down_user(void) { 219void suspend_power_down_user(void) {
278 // code will run multiple times while keyboard is suspended 220 // 当键盘挂起时会被多次调用的代码
279} 221}
280 222
281void suspend_wakeup_init_user(void) { 223void suspend_wakeup_init_user(void) {
282 // code will run on keyboard wakeup 224 // 键盘唤醒时被调用的代码
283} 225}
284``` 226```
285 227
286### 键盘 挂起/唤醒 函数文档 228### 键盘 挂起/唤醒 函数文档
287 229
288* 键盘/修订: `void suspend_power_down_kb(void)` 和`void suspend_wakeup_init_user(void)` 230* 键盘/各子版本:`void suspend_power_down_kb(void)` 和 `void suspend_wakeup_init_user(void)`
289* 局: `void suspend_power_down_kb(void)` 和 `void suspend_wakeup_init_user(void)` 231* 键映`void suspend_power_down_kb(void)` 和 `void suspend_wakeup_init_user(void)`
290 232
291# 层代码 233# 层代码 :id=layer-change-code
292 234
293每当层这个代码。这于层或自定义层处理有用 235每当层换时感知件,或自定义层处理
294 236
295### `layer_state_set_*` 示例实现 237### `layer_state_set_*` 实现示例
296 238
297本例用了Planck键盘示范了如何设置 [RGB背光灯](feature_rgblight.md)与层 239本例,通Planck键盘示范了如何[RGB背光灯](zh-cn/feature_rgblight.md)与层步。
298 240
299```c 241```c
300layer_state_t layer_state_set_user(layer_state_t state) { 242layer_state_t layer_state_set_user(layer_state_t state) {
@@ -311,36 +253,41 @@ layer_state_t layer_state_set_user(layer_state_t state) {
311 case _ADJUST: 253 case _ADJUST:
312 rgblight_setrgb (0x7A, 0x00, 0xFF); 254 rgblight_setrgb (0x7A, 0x00, 0xFF);
313 break; 255 break;
314 default: // for any other layers, or the default layer 256 default: // 默认层及其它层
315 rgblight_setrgb (0x00, 0xFF, 0xFF); 257 rgblight_setrgb (0x00, 0xFF, 0xFF);
316 break; 258 break;
317 } 259 }
318 return state; 260 return state;
319} 261}
320``` 262```
263
264可以通过 `IS_LAYER_ON_STATE(state, layer)` 和 `IS_LAYER_OFF_STATE(state, layer)` 宏来确认常规层的状态。
265
266如果不在 `layer_state_set_*` 函数中,可以通过 `IS_LAYER_ON(layer)` 和 `IS_LAYER_OFF(layer)` 宏来确认全局的层状态。
267
321### `layer_state_set_*` 函数文档 268### `layer_state_set_*` 函数文档
322 269
323* 键盘/修订: `uint32_t layer_state_set_kb(uint32_t state)` 270* 键盘/各子版本:`uint32_t layer_state_set_kb(uint32_t state)`
324* 布局: `layer_state_t layer_state_set_user(layer_state_t state)` 271* 布局: `layer_state_t layer_state_set_user(layer_state_t state)`
325 272
326 273
327该`状`bitmask, 详见[概述](keymap.md#布局的层状态) 274处的 `state` 为当前层的位掩码, 详见[键映概述](zh-cn/keymap.md#keymap-layer-status)
328 275
329 276
330# 掉电保存配置 (EEPROM) 277# 配置的持久存储(EEPROM
331 278
332这会让你的配置长期的保存在键盘中。这些配置保存在你主控的EEPROM里,掉电不会消失。 设置可以用`eeconfig_read_kb`和`eeconfig_read_user`读取,可以用`eeconfig_update_kb`和`eeconfig_update_user`写入。这对于您希望能够切换的功能很有用(比如切换RGB层指示。此外,你可以用`eeconfig_init_kb`和`eeconfig_init_user`来设置EEPROM默认值。 279该功能可以让键盘的配置持久存储下来。这些配置存储在控制器的EEPROM中,即便掉电后依旧可以留存下来。可以通过 `eeconfig_read_kb` 和 `eeconfig_read_user` 来读取,通过 `eeconfig_update_kb` and `eeconfig_update_user` 来进行保存。该功能常用于保存一些开关状态(比如rgb层指示灯)。此外,可以通过 `eeconfig_init_kb` 和 `eeconfig_init_user` 来设置EEPROM的默认配置值。
333 280
334最复杂的部分可能是,有很多方法可以通过EEPROM存储和访问数据,并且并没有用哪种方法是“政治正确”的。你每个功能只有一个双字(四字节)空间。 281复杂的地方是,有很多方法可以存储和访问EEPROM数据,并且没有哪种方法是“正确”的。但是,每个功能只有一个双字(四字节)空间可用。
335 282
336记住EEPROM是有写入寿命的。尽管写入寿命很高,但是并不是只有设置写道EEPROM中。如果你写入频繁,你的MCU寿命将会变短。 283记住EEPROM是有写入寿命的。尽管写入寿命很高,但是并不是只有这些配置信息会写到EEPROM中。如果你写入过于频繁,你的MCU寿命将会急速减少。
337 284
338* 如果您不理解这个例子,那么您可望避使用这个特性,因为它相当复杂。 285* 如果您不理解这个例子,那么您可使用这个特性,因为它相当复杂。
339 286
340### 示例实现 287### 实现示例
341
342本例讲解了如何添加设置,并且读写。本里使用了用户布局。这是一个复杂的函数,有很多事情要做。实际上,它使用了很多上述函数来工作!
343 288
289本例讲解了如何添加并读写设置项。本例使用用户键映射来实现。这是一个复杂的函数,有很多事情要做。实际上,它使用了很多前述的函数来工作!
290(译注:该示例由于英文行文,可能会觉得看得稀里糊涂。实现的功能很简单,即开启了层指示功能(RGB_LYR)时,rgb背光灯会展示当前层的特定颜色用以指示层状态,而触发任何改变rgb背光颜色的键码时,rgb背光灯将回归普通的背光灯角色,不再作为层指示器)
344 291
345在你的keymap.c文件中,将以下代码添加至顶部: 292在你的keymap.c文件中,将以下代码添加至顶部:
346```c 293```c
@@ -354,14 +301,14 @@ typedef union {
354user_config_t user_config; 301user_config_t user_config;
355``` 302```
356 303
357以上代码建立了一个结构体,该结构体可以存储设置并可用于写入EEPROM。如此这般将无需定义变量,因为在结构体中已然定义。要记住`bool` (布尔)值使用1位, `uint8_t`使用8位, `uint16_t`使用16位。你可以混合搭配使用,但是顺序记错可能会招致麻烦,因为那会改变写入写出的值。 304以上代码建立了一个32位的结构体,用于在内存及EEPROM中存储配置项。此时不再需要再单独声明变量,因为都已经在该结构体中定义了。须记住 `bool`(布尔)值占用1位,`uint8_t` 占用8位,`uint16_t` 占用16位。你可以混合搭配使用,但改变这些顺序会因为错误的读写而招致问题。
358 305
359 `layer_state_set_*`函数中使用了`rgb_layer_change`,使用了`keyboard_post_init_user`和`process_record_user`来配置一切。 306我们在 `layer_state_set_*` 函数中会使用 `rgb_layer_change`。通过 `keyboard_post_init_user` 和 `process_record_user` 来配置所需的一切。
360 307
361首先要使用`keyboard_post_init_user,你要加入`eeconfig_read_user()`来填充你刚刚创建的结构体。然后您可以立即使用这个结构来控制您的布局中的功能。就像这样: 308在编写 `keyboard_post_init_user` 时,你需要使用 `eeconfig_read_user()` 来计算并填充你刚刚创建的结构体。然后即可以使用结构体数据来控制键映射中的功能。就像这样:
362```c 309```c
363void keyboard_post_init_user(void) { 310void keyboard_post_init_user(void) {
364 // 调用级别的矩阵初始化 311 // 调用键映级别的矩阵初始化
365 312
366 // 从EEPROM读用户配置 313 // 从EEPROM读用户配置
367 user_config.raw = eeconfig_read_user(); 314 user_config.raw = eeconfig_read_user();
@@ -374,7 +321,7 @@ void keyboard_post_init_user(void) {
374 } 321 }
375} 322}
376``` 323```
377以上函数会在读EEPROM配置后立即使用该设置来设置默认层RGB颜色。"raw"的值是从你上面基于"union"创建的结构体中转换来的。 324以上函数会在读EEPROM配置后立即设置默认层的RGB颜色。"raw"值将被转换为上述创建的实际使用的"union"结构体。
378 325
379```c 326```c
380layer_state_t layer_state_set_user(layer_state_t state) { 327layer_state_t layer_state_set_user(layer_state_t state) {
@@ -398,7 +345,7 @@ layer_state_t layer_state_set_user(layer_state_t state) {
398 return state; 345 return state;
399} 346}
400``` 347```
401这样仅在值使能时会改变RGB背光灯。现在配置这个值, 为`process_record_user`创建一个新键码叫做`RGB_LYR`。我们要确保,如果使用正常的RGB代码,使用上面的示例将其关闭,请将其设置为: 348这样仅在相关值使能时才会改变RGB背光灯。若要配置该值, 为 `process_record_user` 创建一个新键码 `RGB_LYR`。此时我们想实现的是,如果触发了常规的RGB码,以上示例中的逻辑都将不生效,形如:
402```c 349```c
403 350
404bool process_record_user(uint16_t keycode, keyrecord_t *record) { 351bool process_record_user(uint16_t keycode, keyrecord_t *record) {
@@ -407,7 +354,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
407 if (record->event.pressed) { 354 if (record->event.pressed) {
408 // 按下时做点什么 355 // 按下时做点什么
409 } else { 356 } else {
410 // 时做点什么 357 // 时做点什么
411 } 358 }
412 return false; // 跳过此键的进一步处理 359 return false; // 跳过此键的进一步处理
413 case KC_ENTER: 360 case KC_ENTER:
@@ -415,76 +362,116 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
415 if (record->event.pressed) { 362 if (record->event.pressed) {
416 PLAY_SONG(tone_qwerty); 363 PLAY_SONG(tone_qwerty);
417 } 364 }
418 return true; // 让QMK产生回车按下/事件 365 return true; // 让QMK产生回车按下/事件
419 case RGB_LYR: // underglow作为层指示,或正常使 366 case RGB_LYR: // 这允许光灯作为层指示,或正常用
420 if (record->event.pressed) { 367 if (record->event.pressed) {
421 user_config.rgb_layer_change ^= 1; // 切换状态 368 user_config.rgb_layer_change ^= 1; // 切换状态
422 eeconfig_update_user(user_config.raw); // 向EEPROM写入新状态 369 eeconfig_update_user(user_config.raw); // 向EEPROM写入新状态
423 if (user_config.rgb_layer_change) { // 如果层被使能 370 if (user_config.rgb_layer_change) { // 如果层使能
424 layer_state_set(layer_state); // 那么立刻更新层颜色 371 layer_state_set(layer_state); // 那么立刻更新层颜色
425 } 372 }
426 } 373 }
427 return false; 374 return false;
428 case RGB_MODE_FORWARD ... RGB_MODE_GRADIENT: // 对于所有的RGB代码 (see quantum_keycodes.h, L400 可以参) 375 case RGB_MODE_FORWARD ... RGB_MODE_GRADIENT: // 对于所有的RGB代码 (参考 quantum_keycodes.h, 400 )
429 if (record->event.pressed) { //本句失能层指示,假设你…你要把它禁用 376 if (record->event.pressed) { // 本句失能层指示功能,假设你现在要调…你要把它禁用
430 if (user_config.rgb_layer_change) { // 仅当使能时 377 if (user_config.rgb_layer_change) { // 仅当使能时
431 user_config.rgb_layer_change = false; // 失能,然后 378 user_config.rgb_layer_change = false; // 失能,然后
432 eeconfig_update_user(user_config.raw); // 向EEPROM写入设置 379 eeconfig_update_user(user_config.raw); // 向EEPROM写入设置
433 } 380 }
434 } 381 }
435 return true; break; 382 return true; break;
436 default: 383 default:
437 return true; // 其他键正常 384 return true; // 其他键正常处理
438 } 385 }
439} 386}
440``` 387```
441最后你要加入`eeconfig_init_user`函数,所以当EEPROM重置时,可以指定默认值, 甚至自定义操作。想强制重置EEPROM,请用`EEP_RST`键码或[Bootmagic](feature_bootmagic.md)函数。比如,如果要在默认情况下设置RGB层指示,并保存默认值 388最后,须添加 `eeconfig_init_user` 函数,从而当EEPROM重置时,可以指定默认值, 甚至自定义操作。若想强制重置EEPROM,请用 `EEP_RST` 键码或[Bootmagic](zh-cn/feature_bootmagic.md) 功能。比如,在你想重置RGB层指示配置,并保存默认值时。
442 389
443```c 390```c
444void eeconfig_init_user(void) { // EEPROM被重置 391void eeconfig_init_user(void) { // EEPROM被重置
445 user_config.raw = 0; 392 user_config.raw = 0;
446 user_config.rgb_layer_change = true; // 我们想要默认使能 393 user_config.rgb_layer_change = true; // 我们想要默认使能
447 eeconfig_update_user(user_config.raw); // 向EEPROM写入默认值 394 eeconfig_update_user(user_config.raw); // 向EEPROM写入默认值
448 395
449 // use the non noeeprom versions, 还要EEPROM写入这些 396 // 通过使用非'noeeprom'版本的数,可以写入这些配置EEPROM中
450 rgblight_enable(); // 默认使能RGB 397 rgblight_enable(); // 默认使能RGB
451 rgblight_sethsv_cyan(); // 默认设置青色 398 rgblight_sethsv_cyan(); // 默认设置青色
452 rgblight_mode(1); // 默认设置长亮 399 rgblight_mode(1); // 默认设置长亮
453} 400}
454``` 401```
455 402
456然后就完事了。RGB层指示会在你想让它工作时工作。这个设置会一直保存,即便你拔下键盘。如果你使用其他RGB代码,层指示将失能,现在它可以做你所想了。 403一切已就绪,RGB层指示将在需要时生效。这个设置会持久存储,即便是拔下键盘。如果你使用其他RGB码,层指示将失效,从而可以停留在期望的模式及颜色下。
457 404
458### 'EECONFIG' 函数文档 405### 'EECONFIG' 函数文档
459 406
460* 键盘/修订: `void eeconfig_init_kb(void)`, `uint32_t eeconfig_read_kb(void)`和`void eeconfig_update_kb(uint32_t val)` 407* 键盘/各子版本:`void eeconfig_init_kb(void)`, `uint32_t eeconfig_read_kb(void)` 和 `void eeconfig_update_kb(uint32_t val)`
461* 布局: `void eeconfig_init_user(void)`, `uint32_t eeconfig_read_user(void)`和`void eeconfig_update_user(uint32_t val)` 408* 键映射:`void eeconfig_init_user(void)`, `uint32_t eeconfig_read_user(void)` 和 `void eeconfig_update_user(uint32_t val)`
462 409
463`val` 是你想写入EEPROM的值,`eeconfig_read_*`函数会从EEPROM返回一个32位(双字)的值。 410`val` 是你想写入EEPROM的值,`eeconfig_read_*`函数会从EEPROM返回一个32位(双字)的值。
464 411
465# 自定义击键-长按临界值(TAPPING_TERM) 412### 定时执行 :id=deferred-execution
466默认情况下,击键-长按临界值是全球统一的,并且不能通过键进行配置。对于大多数用户来说这很好。但是在有些情况下,对于`LT`键来说按键延时对双功能键的提升更大,可能是因为有些键比其他的键更容易按住。为了不给每个都自定义键码,本功能可以为每个键定义`TAPPING_TERM`。
467
468想使能这个功能的话, 要先在`config.h`加上`#define TAPPING_TERM_PER_KEY`。
469 413
414QMK支持在特定时间间隔后执行回调,以代替手动的计时器管理。
470 415
471## `get_tapping_term`示例 416#### 函数
472 417
473要修基于键`TAPPING TERM`,`keymap.c`如下代码: 418_定时回调函数_ 如下
474 419
475```c 420```c
476uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { 421uint32_t my_callback(uint32_t trigger_time, void *cb_arg) {
477 switch (keycode) { 422 /* 处理了一些工作 */
478 case SFT_T(KC_SPC): 423 bool repeat = my_deferred_functionality();
479 return TAPPING_TERM + 1250; 424 return repeat ? 500 : 0;
480 case LT(1, KC_GRV):
481 return 130;
482 default:
483 return TAPPING_TERM;
484 }
485} 425}
486``` 426```
487 427
488### `get_tapping_term` 函数文档 428第一个参数 `trigger_time` 为预期的执行时间,如果因为其它事情造成了延迟未能在准确的时间点执行,可以利用这个参数“追赶”或者跳过这次间隔,取决于你的目的是什么。
429
430第二个参数 `cb_arg` 为下述的 `defer_exec()` 传入的参数,由此可以获取调用时的状态信息。
431
432返回值为该函数下一次期望被回调的时间间隔毫秒数 -- 若返回 `0` 则会自动被注销掉。上例中,通过执行假想的 `my_deferred_functionality()` 函数来决策回调是否继续下去 -- 若是,则给出一个 `500` 毫秒的延迟计划,否则,返回 `0` 来告知定时处理后台任务该计划已执行完毕。
433
434?> 须留意返回的延时时间是相对原定的触发时间点的,而不是回调执行完的时间点。这样可以防止偶发的执行延迟影响稳定的定时事件计划。
435
436#### 注册定时回调
437
438在定义好回调后,通过如下API进行定时回调注册:
439
440```c
441deferred_token my_token = defer_exec(1500, my_callback, NULL);
442```
443
444第一个参数为执行 `my_callback` 的毫秒时间延迟 -- 上例中为 `1500` 毫秒,即 1.5 秒。
445
446第三个参数为回调执行时传入的 `cb_arg` 参数。须确保该值在回调时依旧有效 -- 局部函数内的变量会在回调执行前就被释放掉因此不能用。如果并不需要这个参数,可以传入 `NULL`。
447
448返回值 `deferred_token` 可被用于在回调执行前取消该定时计划。如果该函数调用失败,会返回 `INVALID_DEFERRED_TOKEN`,一般错误原因是延时值被设置为 `0` 或回调函数参数为 `NULL`,还有一种可能是已有过量的回调在等待被处理 -- 可以按照下述方法修改这个阈值。
449
450#### 延长定时回调时间
451
452由 `defer_exec()` 返回的 `deferred_token` 可以用来修改回调执行所需等待的时延值:
453```c
454// 重新调整 my_token 后续的执行计划为当前时间起800ms后
455extend_deferred_exec(my_token, 800);
456```
457
458#### 取消定时回调
459
460由 `defer_exec()` 返回的 `deferred_token` 可以用来取消掉后续的执行计划:
461```c
462// 取消 my_token 的后续回调
463cancel_deferred_exec(my_token);
464```
465
466一旦 token 被取消了,即视为不再可用。重新使用该 token 是不支持的。
467
468#### 定时回调的限制
469
470可安排的定时回调计划数量是有限的,由 `MAX_DEFERRED_EXECUTORS` 定义的值确定。
471
472如果定时回调注册失败了,可以在对应的键盘或键映射下的 `config.h` 文件中修改该值,比如将默认的 8 改为 16:
473
474```c
475#define MAX_DEFERRED_EXECUTORS 16
476```
489 477
490不像这篇的其他功能,这个不需要quantum或者键盘级别的函数,只要用户级函数即可。