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>
Reviewed-on: https://gerrit.chromium.org/gerrit/34765
Commit-Ready: Randall Spangler <rspangler@chromium.org>
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Tested-by: Randall Spangler <rspangler@chromium.org>
This commit is contained in:
Simon Glass
2012-10-05 09:41:42 -07:00
committed by Gerrit
parent dd2d41003d
commit 1173102ae1

View File

@@ -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,64 @@ 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)) {
keyboard_clear_state();
system_warm_reboot();
}
}
/* Returns 1 if any key is still pressed. 0 if no key is pressed. */
static int check_keys_changed(void)
/* 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");
}
/**
* 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 +305,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,56 +339,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) {
/* 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;
/* 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;
}
}
if (any_change) {
board_keyboard_suppress_noise();
print_state(state, "state");
CPRINTF("[%d keys pressed: ", num_press);
for (c = 0; c < KB_OUTPUTS; c++) {
if (raw_state[c])
CPRINTF(" %02x", raw_state[c]);
else
CPUTS(" --");
#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);
}
CPUTS("]\n");
CPRINTF("\n");
#endif
if (num_press == 3) {
if (check_warm_reboot_keys()) {
keyboard_clear_state();
system_warm_reboot();
return 0;
}
}
check_runtime_keys(state);
if (kb_fifo_add(raw_state) == EC_SUCCESS)
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;
@@ -344,7 +465,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
@@ -352,9 +473,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 */
}
@@ -372,10 +493,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;
}
@@ -411,7 +533,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 */
@@ -440,6 +562,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();
@@ -491,7 +615,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;
@@ -534,12 +658,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");