aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbrickbots <rich@brickbots.com>2020-03-22 06:06:16 -0700
committerGitHub <noreply@github.com>2020-03-23 00:06:16 +1100
commitbfb2f8e0a8f809374fdec102eb02c3bce46a14ee (patch)
tree2bab982f9b1cc9184b58ca9764d1ba09e133335c
parentd8f3c28a3786e7888fe3157c173845107c3ccc95 (diff)
downloadqmk_firmware-bfb2f8e0a8f809374fdec102eb02c3bce46a14ee.tar.gz
qmk_firmware-bfb2f8e0a8f809374fdec102eb02c3bce46a14ee.zip
Add Word Per Minute calculation feature (#8054)
* Add Word Per Minute calculation feature * Fix copyright info * Remove header from quantum.c, setup overloadable keycode inclusion for WPM, update docs * Simplify logic for keycode filtering * Adding link from summary to wpm_feature info * Update docs/feature_wpm.md Typo in function prototype example in docs Co-Authored-By: James Young <18669334+noroadsleft@users.noreply.github.com> * Add WPM transport via i2c Co-authored-by: James Young <18669334+noroadsleft@users.noreply.github.com>
-rw-r--r--common_features.mk5
-rw-r--r--docs/_summary.md1
-rw-r--r--docs/feature_wpm.md25
-rw-r--r--quantum/quantum.c10
-rw-r--r--quantum/quantum.h4
-rw-r--r--quantum/split_common/transport.c27
-rw-r--r--quantum/wpm.c67
-rw-r--r--quantum/wpm.h30
8 files changed, 169 insertions, 0 deletions
diff --git a/common_features.mk b/common_features.mk
index 269ca2b13..50b1127dc 100644
--- a/common_features.mk
+++ b/common_features.mk
@@ -322,6 +322,11 @@ ifeq ($(strip $(USB_HID_ENABLE)), yes)
322 include $(TMK_DIR)/protocol/usb_hid.mk 322 include $(TMK_DIR)/protocol/usb_hid.mk
323endif 323endif
324 324
325ifeq ($(strip $(WPM_ENABLE)), yes)
326 SRC += $(QUANTUM_DIR)/wpm.c
327 OPT_DEFS += -DWPM_ENABLE
328endif
329
325ifeq ($(strip $(ENCODER_ENABLE)), yes) 330ifeq ($(strip $(ENCODER_ENABLE)), yes)
326 SRC += $(QUANTUM_DIR)/encoder.c 331 SRC += $(QUANTUM_DIR)/encoder.c
327 OPT_DEFS += -DENCODER_ENABLE 332 OPT_DEFS += -DENCODER_ENABLE
diff --git a/docs/_summary.md b/docs/_summary.md
index d6186bbf9..4a6e6996e 100644
--- a/docs/_summary.md
+++ b/docs/_summary.md
@@ -80,6 +80,7 @@
80 * [Terminal](feature_terminal.md) 80 * [Terminal](feature_terminal.md)
81 * [Unicode](feature_unicode.md) 81 * [Unicode](feature_unicode.md)
82 * [Userspace](feature_userspace.md) 82 * [Userspace](feature_userspace.md)
83 * [WPM Calculation](feature_wpm.md)
83 84
84 * Hardware Features 85 * Hardware Features
85 * Displays 86 * Displays
diff --git a/docs/feature_wpm.md b/docs/feature_wpm.md
new file mode 100644
index 000000000..12dd08057
--- /dev/null
+++ b/docs/feature_wpm.md
@@ -0,0 +1,25 @@
1# Word Per Minute (WPM) Calculcation
2
3The WPM feature uses time between keystrokes to compute a rolling average words
4per minute rate and makes this available for various uses.
5
6Enable the WPM system by adding this to your `rules.mk`:
7
8 WPM_ENABLE = yes
9
10For split keyboards using soft serial, the computed WPM
11score will be available on the master AND slave half.
12
13## Public Functions
14
15`uint8_t get_current_wpm(void);`
16This function returns the current WPM as an unsigned integer.
17
18
19## Customized keys for WPM calc
20
21By default, the WPM score only includes letters, numbers, space and some
22punctuation. If you want to change the set of characters considered as part of
23the WPM calculation, you can implement `wpm_keycode_user(uint16_t keycode)`
24and return true for any characters you would like included in the calculation,
25or false to not count that particular keycode.
diff --git a/quantum/quantum.c b/quantum/quantum.c
index 749a08eea..49767819d 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -192,6 +192,12 @@ bool process_record_quantum(keyrecord_t *record) {
192 } 192 }
193#endif 193#endif
194 194
195#ifdef WPM_ENABLE
196 if (record->event.pressed) {
197 update_wpm(keycode);
198 }
199#endif
200
195#ifdef TAP_DANCE_ENABLE 201#ifdef TAP_DANCE_ENABLE
196 preprocess_tap_dance(keycode, record); 202 preprocess_tap_dance(keycode, record);
197#endif 203#endif
@@ -645,6 +651,10 @@ void matrix_scan_quantum() {
645 encoder_read(); 651 encoder_read();
646#endif 652#endif
647 653
654#ifdef WPM_ENABLE
655 decay_wpm();
656#endif
657
648#ifdef HAPTIC_ENABLE 658#ifdef HAPTIC_ENABLE
649 haptic_task(); 659 haptic_task();
650#endif 660#endif
diff --git a/quantum/quantum.h b/quantum/quantum.h
index d03ba5942..191407fab 100644
--- a/quantum/quantum.h
+++ b/quantum/quantum.h
@@ -178,6 +178,10 @@ extern layer_state_t layer_state;
178# include "via.h" 178# include "via.h"
179#endif 179#endif
180 180
181#ifdef WPM_ENABLE
182# include "wpm.h"
183#endif
184
181// Function substitutions to ease GPIO manipulation 185// Function substitutions to ease GPIO manipulation
182#if defined(__AVR__) 186#if defined(__AVR__)
183typedef uint8_t pin_t; 187typedef uint8_t pin_t;
diff --git a/quantum/split_common/transport.c b/quantum/split_common/transport.c
index ab421adc4..3234a3ef5 100644
--- a/quantum/split_common/transport.c
+++ b/quantum/split_common/transport.c
@@ -35,6 +35,9 @@ typedef struct _I2C_slave_buffer_t {
35# ifdef ENCODER_ENABLE 35# ifdef ENCODER_ENABLE
36 uint8_t encoder_state[NUMBER_OF_ENCODERS]; 36 uint8_t encoder_state[NUMBER_OF_ENCODERS];
37# endif 37# endif
38# ifdef WPM_ENABLE
39 uint8_t current_wpm;
40# endif
38} I2C_slave_buffer_t; 41} I2C_slave_buffer_t;
39 42
40static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_reg; 43static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_reg;
@@ -43,6 +46,7 @@ static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_re
43# define I2C_RGB_START offsetof(I2C_slave_buffer_t, rgblight_sync) 46# define I2C_RGB_START offsetof(I2C_slave_buffer_t, rgblight_sync)
44# define I2C_KEYMAP_START offsetof(I2C_slave_buffer_t, smatrix) 47# define I2C_KEYMAP_START offsetof(I2C_slave_buffer_t, smatrix)
45# define I2C_ENCODER_START offsetof(I2C_slave_buffer_t, encoder_state) 48# define I2C_ENCODER_START offsetof(I2C_slave_buffer_t, encoder_state)
49# define I2C_WPM_START offsetof(I2C_slave_buffer_t, current_wpm)
46 50
47# define TIMEOUT 100 51# define TIMEOUT 100
48 52
@@ -79,6 +83,14 @@ bool transport_master(matrix_row_t matrix[]) {
79 encoder_update_raw(i2c_buffer->encoder_state); 83 encoder_update_raw(i2c_buffer->encoder_state);
80# endif 84# endif
81 85
86# ifdef WPM_ENABLE
87 uint8_t current_wpm = get_current_wpm();
88 if(current_wpm != i2c_buffer->current_wpm) {
89 if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WPM_START, (void *)&current_wpm, sizeof(current_wpm), TIMEOUT) >= 0) {
90 i2c_buffer->current_wpm = current_wpm;
91 }
92 }
93# endif
82 return true; 94 return true;
83} 95}
84 96
@@ -102,6 +114,10 @@ void transport_slave(matrix_row_t matrix[]) {
102# ifdef ENCODER_ENABLE 114# ifdef ENCODER_ENABLE
103 encoder_state_raw(i2c_buffer->encoder_state); 115 encoder_state_raw(i2c_buffer->encoder_state);
104# endif 116# endif
117
118# ifdef WPM_ENABLE
119 set_current_wpm(i2c_buffer->current_wpm);
120# endif
105} 121}
106 122
107void transport_master_init(void) { i2c_init(); } 123void transport_master_init(void) { i2c_init(); }
@@ -126,6 +142,9 @@ typedef struct _Serial_m2s_buffer_t {
126# ifdef BACKLIGHT_ENABLE 142# ifdef BACKLIGHT_ENABLE
127 uint8_t backlight_level; 143 uint8_t backlight_level;
128# endif 144# endif
145# ifdef WPM_ENABLE
146 uint8_t current_wpm;
147# endif
129} Serial_m2s_buffer_t; 148} Serial_m2s_buffer_t;
130 149
131# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) 150# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
@@ -228,6 +247,10 @@ bool transport_master(matrix_row_t matrix[]) {
228 encoder_update_raw((uint8_t *)serial_s2m_buffer.encoder_state); 247 encoder_update_raw((uint8_t *)serial_s2m_buffer.encoder_state);
229# endif 248# endif
230 249
250# ifdef WPM_ENABLE
251 // Write wpm to slave
252 serial_m2s_buffer.current_wpm = get_current_wpm();
253# endif
231 return true; 254 return true;
232} 255}
233 256
@@ -244,6 +267,10 @@ void transport_slave(matrix_row_t matrix[]) {
244# ifdef ENCODER_ENABLE 267# ifdef ENCODER_ENABLE
245 encoder_state_raw((uint8_t *)serial_s2m_buffer.encoder_state); 268 encoder_state_raw((uint8_t *)serial_s2m_buffer.encoder_state);
246# endif 269# endif
270
271# ifdef WPM_ENABLE
272 set_current_wpm(serial_m2s_buffer.current_wpm);
273# endif
247} 274}
248 275
249#endif 276#endif
diff --git a/quantum/wpm.c b/quantum/wpm.c
new file mode 100644
index 000000000..d4c971f31
--- /dev/null
+++ b/quantum/wpm.c
@@ -0,0 +1,67 @@
1/*
2 * Copyright 2020 Richard Sutherland (rich@brickbots.com)
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "wpm.h"
19
20//WPM Stuff
21static uint8_t current_wpm = 0;
22static uint8_t latest_wpm = 0;
23static uint16_t wpm_timer = 0;
24
25//This smoothing is 40 keystrokes
26static const float wpm_smoothing = 0.0487;
27
28void set_current_wpm(uint8_t new_wpm) { current_wpm = new_wpm; }
29
30uint8_t get_current_wpm(void) { return current_wpm; }
31
32bool wpm_keycode(uint16_t keycode) { return wpm_keycode_kb(keycode); }
33
34__attribute__((weak)) bool wpm_keycode_kb(uint16_t keycode) { return wpm_keycode_user(keycode); }
35
36__attribute__((weak)) bool wpm_keycode_user(uint16_t keycode) {
37
38 if ((keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX) || (keycode >= QK_LAYER_TAP && keycode <= QK_LAYER_TAP_MAX) || (keycode >= QK_MODS && keycode <= QK_MODS_MAX)) {
39 keycode = keycode & 0xFF;
40 } else if (keycode > 0xFF) {
41 keycode = 0;
42 }
43 if((keycode >= KC_A && keycode <= KC_0) || (keycode >= KC_TAB && keycode <= KC_SLASH)) {
44 return true;
45 }
46
47 return false;
48}
49
50
51void update_wpm(uint16_t keycode) {
52 if(wpm_keycode(keycode)) {
53 if(wpm_timer > 0) {
54 latest_wpm = 60000 / timer_elapsed(wpm_timer) / 5;
55 current_wpm = (latest_wpm - current_wpm) * wpm_smoothing + current_wpm;
56 }
57 wpm_timer = timer_read();
58 }
59}
60
61void decay_wpm(void) {
62 if (timer_elapsed(wpm_timer) > 1000) {
63 current_wpm = (0 - current_wpm) * wpm_smoothing +
64 current_wpm;
65 wpm_timer = timer_read();
66 }
67}
diff --git a/quantum/wpm.h b/quantum/wpm.h
new file mode 100644
index 000000000..fa0b6d128
--- /dev/null
+++ b/quantum/wpm.h
@@ -0,0 +1,30 @@
1/*
2 * Copyright 2020 Richard Sutherland (rich@brickbots.com)
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#pragma once
19
20#include "quantum.h"
21
22bool wpm_keycode(uint16_t keycode);
23bool wpm_keycode_kb(uint16_t keycode);
24bool wpm_keycode_user(uint16_t keycode);
25
26void set_current_wpm(uint8_t);
27uint8_t get_current_wpm(void);
28void update_wpm(uint16_t);
29
30void decay_wpm(void);