diff options
author | Simon Glass <sjg@chromium.org> | 2012-10-05 09:41:42 -0700 |
---|---|---|
committer | Randall Spangler <rspangler@chromium.org> | 2012-10-05 15:51:04 -0700 |
commit | bd2fceaeb6ac1879127283fe5976a45a3f89a9c1 (patch) | |
tree | 3136d71e9ed51c4883fa21aaadeef737a7e68b7b | |
parent | acf3bc28d947dc886237d881a7619a76ed2a9b72 (diff) | |
download | chrome-ec-bd2fceaeb6ac1879127283fe5976a45a3f89a9c1.tar.gz |
stm32: Implement keyboard debouncing
This brings Randall's key debounce logic into STM32. We need to
rationalize the code, but for this morning...
This should fix problems with double keypresses and missing keypresses.
BUG=chrome-os-partner:12179
BRANCH=snow
TEST=manual
- type quickly; should work
- run your finger really quickly over the keyboard; should be able to
see keys which don't show up because you didn't press them long enough
- run your finger quickly from 1 to 0; numbers should show up in order
(some may be missing if you sweep too fast. there is a point where if
two keys are hit within 1.7ms of each other they can be swapped, but any
slower than that and they should never be out of order)
- mash your face into the keyboard to cause ghosting; should see only a few
keys pressed
Change-Id: I6b164a17de1b4dd698f9b45a3852fd3b6c084e0a
Signed-off-by: Simon Glass <sjg@chromium.org>
(cherry picked from commit 6b1c82ad75ffc9a509eb3c3240fd96bf400ee73b)
Conflicts:
chip/stm32/keyboard_scan.c
Change-Id: I45a1b448cbd24c2421f8e39e3cb033f4c4f63a01
Reviewed-on: https://gerrit.chromium.org/gerrit/34773
Reviewed-by: David Hendricks <dhendrix@chromium.org>
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Tested-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r-- | chip/stm32/keyboard_scan.c | 231 |
1 files changed, 180 insertions, 51 deletions
diff --git a/chip/stm32/keyboard_scan.c b/chip/stm32/keyboard_scan.c index 0dd3877f9d..73e20db356 100644 --- a/chip/stm32/keyboard_scan.c +++ b/chip/stm32/keyboard_scan.c @@ -33,13 +33,22 @@ enum COL_INDEX { /* 0 ~ 12 for the corresponding column */ }; +#define KB_INPUTS 8 + +#define SCAN_TIME_COUNT 32 + /* 15:14, 12:8, 2 */ #define IRQ_MASK 0xdf04 static struct mutex scanning_enabled; -/* The keyboard state from the last read */ -static uint8_t raw_state[KB_OUTPUTS]; +static uint8_t debounced_state[KB_OUTPUTS]; /* Debounced key matrix */ +static uint8_t prev_state[KB_OUTPUTS]; /* Matrix from previous scan */ +static uint8_t debouncing[KB_OUTPUTS]; /* Mask of keys being debounced */ +static uint32_t scan_time[SCAN_TIME_COUNT]; /* Times of last scans */ +static int scan_time_index; /* Current scan_time[] index */ +/* Index into scan_time[] when each key started debouncing */ +static uint8_t scan_edge_index[KB_OUTPUTS][KB_INPUTS]; /* Key masks for special boot keys */ #define MASK_INDEX_ESC 1 @@ -97,6 +106,8 @@ static struct ec_mkbp_config config = { .poll_timeout_us = 100 * 1000, .min_post_scan_delay_us = 1000, .output_settle_us = 50, + .debounce_down_us = 9000, + .debounce_up_us = 30000, .fifo_max_depth = KB_FIFO_DEPTH, }; @@ -228,24 +239,62 @@ void enter_polling_mode(void) select_column(COL_TRI_STATE_ALL); } -static int check_warm_reboot_keys(void) +/** + * Check special runtime key combinations. + * + * @param state Keyboard state to use when checking keys. + */ +static void check_runtime_keys(const uint8_t *state) { - if (raw_state[MASK_INDEX_KEYR] == MASK_VALUE_KEYR && - raw_state[MASK_INDEX_VOL_UP] == MASK_VALUE_VOL_UP && - (raw_state[MASK_INDEX_RIGHT_ALT] == MASK_VALUE_RIGHT_ALT || - raw_state[MASK_INDEX_LEFT_ALT] == MASK_VALUE_LEFT_ALT)) - return 1; + int num_press; + int c; - return 0; + /* Count number of key pressed */ + for (c = num_press = 0; c < KB_OUTPUTS; c++) { + if (state[c]) + ++num_press; + } + + if (num_press != 3) + return; + + if (state[MASK_INDEX_KEYR] == MASK_VALUE_KEYR && + state[MASK_INDEX_VOL_UP] == MASK_VALUE_VOL_UP && + (state[MASK_INDEX_RIGHT_ALT] == MASK_VALUE_RIGHT_ALT || + state[MASK_INDEX_LEFT_ALT] == MASK_VALUE_LEFT_ALT)) + system_warm_reboot(); +} + +/* Print the keyboard state. */ +static void print_state(const uint8_t *state, const char *msg) +{ + int c; + + CPRINTF("[%T KB %s:", msg); + for (c = 0; c < KB_OUTPUTS; c++) { + if (state[c]) + CPRINTF(" %02x", state[c]); + else + CPUTS(" --"); + } + CPUTS("]\n"); } -/* Returns 1 if any key is still pressed. 0 if no key is pressed. */ -static int check_keys_changed(void) +/** + * Read the raw keyboard matrix state. + * + * Used in pre-init, so must not make task-switching-dependent calls; udelay() + * is ok because it's a spin-loop. + * + * @param state Destination for new state (must be KB_OUTPUTS long). + * + * @return 1 if at least one key is pressed, else zero. + */ +static int read_matrix(uint8_t *state) { int c; uint8_t r; - int change = 0; - int num_press = 0; + int pressed = 0; for (c = 0; c < KB_OUTPUTS; c++) { uint16_t tmp; @@ -254,6 +303,10 @@ static int check_keys_changed(void) select_column(c); udelay(config.output_settle_us); + /* + * TODO(sjg@chromium.org): This code can be improved by doing + * the job in 3 shift/or operations. + */ r = 0; tmp = STM32_GPIO_IDR(C); /* KB_COL00:04 = PC8:12 */ @@ -284,53 +337,122 @@ static int check_keys_changed(void) #ifdef OR_WITH_CURRENT_STATE_FOR_TESTING /* KLUDGE - or current state in, so we can make sure * all the lines are hooked up */ - r |= raw_state[c]; + r |= state[c]; #endif - /* Check for changes */ - if (r != raw_state[c]) { - raw_state[c] = r; - change = 1; - } + state[c] = r; + pressed |= r; } select_column(COL_TRI_STATE_ALL); - /* Count number of key pressed */ + return pressed ? 1 : 0; +} + +/** + * Update keyboard state using low-level interface to read keyboard. + * + * @param state Keyboard state to update. + * + * @return 1 if any key is still pressed, 0 if no key is pressed. + */ +static int check_keys_changed(uint8_t *state) +{ + int any_pressed = 0; + int c, i; + int any_change = 0; + uint8_t new_state[KB_OUTPUTS]; + uint32_t tnow = get_time().le.lo; + + /* Save the current scan time */ + if (++scan_time_index >= SCAN_TIME_COUNT) + scan_time_index = 0; + scan_time[scan_time_index] = tnow; + + /* Read the raw key state */ + any_pressed = read_matrix(new_state); + + /* Check for changes between previous scan and this one */ for (c = 0; c < KB_OUTPUTS; c++) { - if (raw_state[c]) - ++num_press; + int diff = new_state[c] ^ prev_state[c]; + + if (!diff) + continue; + + for (i = 0; i < KB_INPUTS; i++) { + if (diff & (1 << i)) + scan_edge_index[c][i] = scan_time_index; + } + + debouncing[c] |= diff; + prev_state[c] = new_state[c]; } - if (change) { - board_keyboard_suppress_noise(); + /* Check for keys which are done debouncing */ + for (c = 0; c < KB_OUTPUTS; c++) { + int debc = debouncing[c]; + + if (!debc) + continue; + + for (i = 0; i < KB_INPUTS; i++) { + int mask = 1 << i; + int new_mask = new_state[c] & mask; - CPRINTF("[%d keys pressed: ", num_press); - for (c = 0; c < KB_OUTPUTS; c++) { - if (raw_state[c]) - CPRINTF(" %02x", raw_state[c]); - else - CPUTS(" --"); + /* Are we done debouncing this key? */ + if (!(debc & mask)) + continue; /* Not debouncing this key */ + if (tnow - scan_time[scan_edge_index[c][i]] < + (new_mask ? config.debounce_down_us : + config.debounce_up_us)) + continue; /* Not done debouncing */ + + debouncing[c] &= ~mask; + + /* Did the key change from its previous state? */ + if ((state[c] & mask) == new_mask) + continue; /* No */ + + state[c] ^= mask; + any_change = 1; } - CPUTS("]\n"); + } - if (num_press == 3) { - if (check_warm_reboot_keys()) - system_warm_reboot(); + if (any_change) { + board_keyboard_suppress_noise(); + print_state(state, "state"); + +#ifdef PRINT_SCAN_TIMES + /* Print delta times from now back to each previous scan */ + for (i = 0; i < SCAN_TIME_COUNT; i++) { + int tnew = scan_time[ + (SCAN_TIME_COUNT + scan_time_index - i) % + SCAN_TIME_COUNT]; + CPRINTF(" %d", tnow - tnew); } + CPRINTF("\n"); +#endif - if (kb_fifo_add(raw_state) == EC_SUCCESS) + check_runtime_keys(state); + + if (kb_fifo_add(state) == EC_SUCCESS) board_interrupt_host(1); else CPRINTF("dropped keystroke\n"); } - return num_press ? 1 : 0; + return any_pressed; } - -/* Returns non-zero if the user has triggered a recovery reset by pushing - * Power + Refresh + ESC. */ -static int check_recovery_key(void) +/* + * Check if the user has triggered a recovery reset + * + * Pressing Power + Refresh + ESC. triggers a recovery reset. Here we check + * for this. + * + * @param state Keyboard state to check + * @return 1 if there is a recovery reset, else 0 + */ +static int check_recovery_key(const uint8_t *state) { int c; @@ -341,7 +463,7 @@ static int check_recovery_key(void) /* cold boot : Power + Refresh were pressed, * check if ESC is also pressed for recovery. */ - if (!(raw_state[MASK_INDEX_ESC] & MASK_VALUE_ESC)) + if (!(state[MASK_INDEX_ESC] & MASK_VALUE_ESC)) return 0; /* Make sure only other allowed keys are pressed. This protects @@ -349,9 +471,9 @@ static int check_recovery_key(void) * your keyboard. Currently, only the requested key and ESC are * allowed. */ for (c = 0; c < KB_OUTPUTS; c++) { - if (raw_state[c] && - (c != MASK_INDEX_ESC || raw_state[c] != MASK_VALUE_ESC) && - (c != MASK_INDEX_REFRESH || raw_state[c] != MASK_VALUE_REFRESH)) + if (state[c] && + (c != MASK_INDEX_ESC || state[c] != MASK_VALUE_ESC) && + (c != MASK_INDEX_REFRESH || state[c] != MASK_VALUE_REFRESH)) return 0; /* Additional disallowed key pressed */ } @@ -369,10 +491,11 @@ int keyboard_scan_init(void) select_column(COL_TRI_STATE_ALL); /* Initialize raw state */ - check_keys_changed(); + read_matrix(debounced_state); + memcpy(prev_state, debounced_state, sizeof(prev_state)); /* is recovery key pressed on cold startup ? */ - check_recovery_key(); + check_recovery_key(debounced_state); return EC_SUCCESS; } @@ -408,7 +531,7 @@ static void scan_keyboard(void) /* Scan immediately, with no delay */ mutex_lock(&scanning_enabled); - keys_changed = check_keys_changed(); + keys_changed = check_keys_changed(debounced_state); mutex_unlock(&scanning_enabled); /* Wait a bit before scanning again */ @@ -437,6 +560,8 @@ void keyboard_scan_task(void) gpio_enable_interrupt(GPIO_KB_IN06); gpio_enable_interrupt(GPIO_KB_IN07); + print_state(debounced_state, "init state"); + while (1) { if (config.flags & EC_MKBP_FLAGS_ENABLE) { scan_keyboard(); @@ -488,7 +613,7 @@ static int keyboard_get_info(struct host_cmd_handler_args *args) { struct ec_response_mkbp_info *r = args->response; - r->rows = 8; + r->rows = KB_INPUTS; r->cols = KB_OUTPUTS; r->switches = 0; @@ -531,12 +656,16 @@ static int command_keyboard_press(int argc, char **argv) if (*e || p < 0 || p > 1) return EC_ERROR_PARAM3; + /* + * TODO(sjg@chromium.org): This ignores debouncing, so is a bit + * dodgy and might have strange side-effects on real key scans. + */ if (p) - raw_state[c] |= (1 << r); + debounced_state[c] |= (1 << r); else - raw_state[c] &= ~(1 << r); + debounced_state[c] &= ~(1 << r); - if (kb_fifo_add(raw_state) == EC_SUCCESS) + if (kb_fifo_add(debounced_state) == EC_SUCCESS) board_interrupt_host(1); else ccprintf("dropped keystroke\n"); |