diff options
| -rw-r--r-- | common_features.mk | 5 | ||||
| -rw-r--r-- | docs/_summary.md | 1 | ||||
| -rw-r--r-- | docs/feature_wpm.md | 25 | ||||
| -rw-r--r-- | quantum/quantum.c | 10 | ||||
| -rw-r--r-- | quantum/quantum.h | 4 | ||||
| -rw-r--r-- | quantum/split_common/transport.c | 27 | ||||
| -rw-r--r-- | quantum/wpm.c | 67 | ||||
| -rw-r--r-- | quantum/wpm.h | 30 |
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 |
| 323 | endif | 323 | endif |
| 324 | 324 | ||
| 325 | ifeq ($(strip $(WPM_ENABLE)), yes) | ||
| 326 | SRC += $(QUANTUM_DIR)/wpm.c | ||
| 327 | OPT_DEFS += -DWPM_ENABLE | ||
| 328 | endif | ||
| 329 | |||
| 325 | ifeq ($(strip $(ENCODER_ENABLE)), yes) | 330 | ifeq ($(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 | |||
| 3 | The WPM feature uses time between keystrokes to compute a rolling average words | ||
| 4 | per minute rate and makes this available for various uses. | ||
| 5 | |||
| 6 | Enable the WPM system by adding this to your `rules.mk`: | ||
| 7 | |||
| 8 | WPM_ENABLE = yes | ||
| 9 | |||
| 10 | For split keyboards using soft serial, the computed WPM | ||
| 11 | score will be available on the master AND slave half. | ||
| 12 | |||
| 13 | ## Public Functions | ||
| 14 | |||
| 15 | `uint8_t get_current_wpm(void);` | ||
| 16 | This function returns the current WPM as an unsigned integer. | ||
| 17 | |||
| 18 | |||
| 19 | ## Customized keys for WPM calc | ||
| 20 | |||
| 21 | By default, the WPM score only includes letters, numbers, space and some | ||
| 22 | punctuation. If you want to change the set of characters considered as part of | ||
| 23 | the WPM calculation, you can implement `wpm_keycode_user(uint16_t keycode)` | ||
| 24 | and return true for any characters you would like included in the calculation, | ||
| 25 | or 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__) |
| 183 | typedef uint8_t pin_t; | 187 | typedef 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 | ||
| 40 | static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_reg; | 43 | static 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 *)¤t_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 | ||
| 107 | void transport_master_init(void) { i2c_init(); } | 123 | void 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 | ||
| 21 | static uint8_t current_wpm = 0; | ||
| 22 | static uint8_t latest_wpm = 0; | ||
| 23 | static uint16_t wpm_timer = 0; | ||
| 24 | |||
| 25 | //This smoothing is 40 keystrokes | ||
| 26 | static const float wpm_smoothing = 0.0487; | ||
| 27 | |||
| 28 | void set_current_wpm(uint8_t new_wpm) { current_wpm = new_wpm; } | ||
| 29 | |||
| 30 | uint8_t get_current_wpm(void) { return current_wpm; } | ||
| 31 | |||
| 32 | bool 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 | |||
| 51 | void 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 | |||
| 61 | void 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 | |||
| 22 | bool wpm_keycode(uint16_t keycode); | ||
| 23 | bool wpm_keycode_kb(uint16_t keycode); | ||
| 24 | bool wpm_keycode_user(uint16_t keycode); | ||
| 25 | |||
| 26 | void set_current_wpm(uint8_t); | ||
| 27 | uint8_t get_current_wpm(void); | ||
| 28 | void update_wpm(uint16_t); | ||
| 29 | |||
| 30 | void decay_wpm(void); | ||
