summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlec Berg <alecaberg@chromium.org>2014-09-29 11:45:01 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-10-18 07:38:26 +0000
commit316f369f1cc859880485cab7dd760fa19b48e70d (patch)
treec35775e93a2cf61fac9b0f2a3490812807d145c9
parent5d4846ee520b6f1e743a6e58537a2ab2fac0cafc (diff)
downloadchrome-ec-316f369f1cc859880485cab7dd760fa19b48e70d.tar.gz
samus: add tap for battery
Adds double tap detection for samus. When user double taps in S3 or lower to show battery state of charge on lightbar. BUG=chrome-os-partner:29041 BRANCH=samus TEST=make buildall Tap the lid in S3 or lower. Change-Id: Ic5f4709bdee2472cb7e91717318337b04bae1fc8 Signed-off-by: Alec Berg <alecaberg@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/221965 Reviewed-by: David Schneider <dnschneid@chromium.org>
-rw-r--r--board/samus/board.h2
-rw-r--r--board/samus/build.mk2
-rw-r--r--board/samus/gesture.c334
-rw-r--r--common/motion_sense.c43
-rw-r--r--include/accelgyro.h2
-rw-r--r--include/config.h8
-rw-r--r--include/gesture.h31
7 files changed, 413 insertions, 9 deletions
diff --git a/board/samus/board.h b/board/samus/board.h
index b4318cd0cb..394b621d74 100644
--- a/board/samus/board.h
+++ b/board/samus/board.h
@@ -47,6 +47,8 @@
#define CONFIG_CHARGER_INPUT_CURRENT 512
#define CONFIG_CHARGER_DISCHARGE_ON_AC
#define CONFIG_FANS 2
+#define CONFIG_GESTURE_DETECTION
+#define CONFIG_GESTURE_SAMPLING_INTERVAL_MS 5
#define CONFIG_PECI_TJMAX 100
#define CONFIG_PWM
#define CONFIG_PWM_KBLIGHT
diff --git a/board/samus/build.mk b/board/samus/build.mk
index 153a280b76..3e6368aaeb 100644
--- a/board/samus/build.mk
+++ b/board/samus/build.mk
@@ -9,4 +9,4 @@
# the IC is TI Stellaris LM4
CHIP:=lm4
-board-y=board.o power_sequence.o panel.o extpower.o
+board-y=board.o power_sequence.o panel.o extpower.o gesture.o
diff --git a/board/samus/gesture.c b/board/samus/gesture.c
new file mode 100644
index 0000000000..853a568bdb
--- /dev/null
+++ b/board/samus/gesture.c
@@ -0,0 +1,334 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Board specific gesture recognition */
+
+#include "accelgyro.h"
+#include "common.h"
+#include "console.h"
+#include "lid_switch.h"
+#include "lightbar.h"
+#include "motion_sense.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+
+/* Console output macros */
+#define CPUTS(outstr) cputs(CC_MOTION_SENSE, outstr)
+#define CPRINTS(format, args...) cprints(CC_MOTION_SENSE, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_MOTION_SENSE, format, ## args)
+
+/* Output datarate for tap sensor (in milli-Hz) */
+#define TAP_ODR (1000000 / CONFIG_GESTURE_SAMPLING_INTERVAL_MS)
+
+/*
+ * Double tap detection parameters
+ * Double tap works by looking for two isolated Z-axis accelerometer impulses
+ * preceded and followed by relatively calm periods of accelerometer motion.
+ *
+ * Define an outer and inner window. The inner window specifies how
+ * long the tap impulse is expected to last. The outer window specifies the
+ * period before the initial tap impluse and after the final tap impulse for
+ * which to check for relatively calm periods. In between the two impulses
+ * there is a minimum and maximum interstice time allowed.
+ */
+#define OUTER_WINDOW_T 200
+#define INNER_WINDOW_T 30
+#define MIN_INTERSTICE_T 120
+#define MAX_INTERSTICE_T 500
+
+#define OUTER_WINDOW (OUTER_WINDOW_T / CONFIG_GESTURE_SAMPLING_INTERVAL_MS)
+#define INNER_WINDOW (INNER_WINDOW_T / CONFIG_GESTURE_SAMPLING_INTERVAL_MS)
+#define MIN_INTERSTICE (MIN_INTERSTICE_T / CONFIG_GESTURE_SAMPLING_INTERVAL_MS)
+#define MAX_INTERSTICE (MAX_INTERSTICE_T / CONFIG_GESTURE_SAMPLING_INTERVAL_MS)
+#define MAX_WINDOW OUTER_WINDOW
+
+/* State machine states for detecting double tap */
+enum tap_states {
+ /* Look for calm before the storm */
+ TAP_IDLE,
+ /* Record first Z impulse */
+ TAP_IMPULSE_1,
+
+ /* Eye of the storm, expect Z motion to drop and then suddenly spike */
+ TAP_INTERSTICE_DROP,
+ TAP_INTERSTICE_RISE,
+
+ /* Record second Z impulse */
+ TAP_IMPULSE_2,
+ /* Should be quiet after the storm */
+ TAP_AFTER_EVENT
+};
+
+/* Tap sensor to use */
+static struct motion_sensor_t *sensor = &motion_sensors[0];
+
+/* Tap state information */
+static int history_z[MAX_WINDOW]; /* Changes in Z */
+static int history_xy[MAX_WINDOW]; /* Changes in X and Y */
+static int state, history_idx;
+static int history_initialized;
+static int tap_debug;
+
+/* Tap detection flag */
+static int tap_detection;
+static int saved_odr;
+
+/*
+ * TODO(crosbug.com/p/33102): Cleanup this function: break into multiple
+ * functions and generalize so it can be used for other boards.
+ */
+static int gesture_tap_for_battery(void)
+{
+ /* Current and previous accel x,y,z */
+ int x, y, z;
+ static int x_p, y_p, z_p;
+
+ /* Number of iterations in this state */
+ static int state_cnt;
+
+ /*
+ * Running sums of data diffs for inner and outer windows.
+ * Z data kept seperate from X and Y data
+ */
+ static int sum_z_inner, sum_z_outer, sum_xy_inner, sum_xy_outer;
+
+ /* Total variation in each signal, normalized for window size */
+ int delta_z_outer, delta_z_inner, delta_xy_outer, delta_xy_inner;
+
+ /* Max variation seen during tap event and state cnts since max */
+ static int delta_z_inner_max;
+ static int cnts_since_max;
+
+ /* Interstice Z motion thresholds */
+ static int z_drop_thresh, z_rise_thresh;
+
+ int history_idx_inner, state_p;
+ int ret = 0;
+
+ /* Get data */
+ x = sensor->xyz[0];
+ y = sensor->xyz[1];
+ z = sensor->xyz[2];
+
+ /*
+ * Calculate history of change in Z sensor and keeping
+ * running sums for the past.
+ */
+ history_idx_inner = history_idx - INNER_WINDOW;
+ if (history_idx_inner < 0)
+ history_idx_inner += MAX_WINDOW;
+ sum_z_inner -= history_z[history_idx_inner];
+ sum_z_outer -= history_z[history_idx];
+ history_z[history_idx] = ABS(z - z_p);
+ sum_z_inner += history_z[history_idx];
+ sum_z_outer += history_z[history_idx];
+
+ /*
+ * Calculate history of change in X and Y sensors combined
+ * and keep a running sum of the change over the past.
+ */
+ sum_xy_inner -= history_xy[history_idx_inner];
+ sum_xy_outer -= history_xy[history_idx];
+ history_xy[history_idx] = ABS(x - x_p) + ABS(y - y_p);
+ sum_xy_inner += history_xy[history_idx];
+ sum_xy_outer += history_xy[history_idx];
+
+ /* Increment history index */
+ history_idx = (history_idx == MAX_WINDOW - 1) ? 0 : (history_idx + 1);
+
+ /* Store previous X, Y, Z data */
+ x_p = x;
+ y_p = y;
+ z_p = z;
+
+ /* Ignore data until we fill history buffer and wrap around */
+ if (history_idx == 0)
+ history_initialized = 1;
+ if (history_initialized == 0)
+ return 0;
+
+ /*
+ * Normalize data based on window size and isolate outer and inner
+ * window data.
+ */
+ delta_z_outer = (sum_z_outer - sum_z_inner) * 1000 /
+ (OUTER_WINDOW - INNER_WINDOW);
+ delta_z_inner = sum_z_inner * 1000 / INNER_WINDOW;
+ delta_xy_outer = (sum_xy_outer - sum_xy_inner) * 1000 /
+ (OUTER_WINDOW - INNER_WINDOW);
+ delta_xy_inner = sum_xy_inner * 1000 / INNER_WINDOW;
+
+ state_cnt++;
+ state_p = state;
+
+ switch (state) {
+ case TAP_IDLE:
+ /* Look for a sudden increase in Z movement */
+ if (delta_z_inner > 13 * delta_z_outer &&
+ delta_z_inner > 1 * delta_xy_inner) {
+ delta_z_inner_max = delta_z_inner;
+ state_cnt = TAP_IDLE;
+ state = TAP_IMPULSE_1;
+ }
+ break;
+
+ case TAP_IMPULSE_1:
+ /* Find the peak inner window of Z movement */
+ if (delta_z_inner > delta_z_inner_max) {
+ delta_z_inner_max = delta_z_inner;
+ cnts_since_max = state_cnt;
+ }
+
+ /* After inner window has passed, move to next state */
+ if (state_cnt >= INNER_WINDOW) {
+ state = TAP_INTERSTICE_DROP;
+ z_drop_thresh = delta_z_inner_max / 12;
+ z_rise_thresh = delta_z_inner_max / 3;
+ state_cnt += INNER_WINDOW - cnts_since_max;
+ }
+ break;
+
+ case TAP_INTERSTICE_DROP:
+ /* Check for z motion to go back down first */
+ if (delta_z_inner < z_drop_thresh)
+ state = TAP_INTERSTICE_RISE;
+
+ if (state_cnt > MAX_INTERSTICE)
+ state = TAP_IDLE;
+
+ break;
+
+ case TAP_INTERSTICE_RISE:
+ /* Then, check for z motion to go back up */
+ if (delta_z_inner > z_rise_thresh) {
+ if (state_cnt < MIN_INTERSTICE) {
+ state = TAP_IDLE;
+ } else {
+ delta_z_inner_max = delta_z_inner;
+ state_cnt = 0;
+ state = TAP_IMPULSE_2;
+ }
+ }
+
+ if (state_cnt > MAX_INTERSTICE)
+ state = TAP_IDLE;
+ break;
+
+ case TAP_IMPULSE_2:
+ /* Find the peak inner window of Z movement */
+ if (delta_z_inner > delta_z_inner_max) {
+ delta_z_inner_max = delta_z_inner;
+ cnts_since_max = state_cnt;
+ }
+
+ /* After inner window has passed, move to next state */
+ if (state_cnt >= INNER_WINDOW) {
+ state = TAP_AFTER_EVENT;
+ state_cnt += INNER_WINDOW - cnts_since_max;
+ }
+
+ case TAP_AFTER_EVENT:
+ /* Check for small Z movement after the event */
+ if (state_cnt < OUTER_WINDOW)
+ break;
+
+ if (2 * delta_z_inner_max > 3 * delta_z_outer &&
+ delta_z_outer > 1 * delta_xy_outer)
+ ret = 1;
+
+ state = TAP_IDLE;
+ break;
+ }
+
+ /* On state transitions, print debug info */
+ if (state != state_p && tap_debug) {
+ /* make sure we don't divide by 0 */
+ if (delta_z_outer == 0 || delta_xy_inner == 0)
+ CPRINTS("tap st %d->%d, error div by 0",
+ state_p, state);
+ else
+ CPRINTS("tap st %d->%d, st_cnt %-3d, Z_in:Z_out %-3d, "
+ "Z_in:XY_in %-3d, dZ_in %-8.3d, "
+ "dZ_in_max %-8.3d, dZ_out %-8.3d",
+ state_p, state, state_cnt,
+ delta_z_inner / delta_z_outer,
+ delta_z_inner / delta_xy_inner,
+ delta_z_inner, delta_z_inner_max,
+ delta_z_outer);
+ }
+
+ return ret;
+}
+
+void gesture_chipset_resume(void)
+{
+ /* Restore ODR and disable tap detection */
+ tap_detection = 0;
+ sensor->drv->set_data_rate(sensor, saved_odr, 1);
+}
+
+void gesture_chipset_suspend(void)
+{
+ /* Record active ODR, set ODR to desired value */
+ sensor->drv->get_data_rate(sensor, &saved_odr);
+ sensor->drv->set_data_rate(sensor, TAP_ODR, 1);
+
+ /*
+ * Clear tap init and history index so that we have to
+ * record a whole new set of data, and enable tap detection
+ */
+ history_initialized = 0;
+ state = 0;
+ history_idx = 0;
+ tap_detection = 1;
+}
+
+void gesture_chipset_shutdown(void)
+{
+ sensor->odr = TAP_ODR;
+ saved_odr = sensor->default_odr;
+}
+
+void gesture_calc(void)
+{
+ /* Only check for gesture if lid is closed and tap detection is on */
+ if (!tap_detection || lid_is_open())
+ return;
+
+ if (gesture_tap_for_battery()) {
+ CPRINTS("Double Tap!");
+ lightbar_sequence(LIGHTBAR_TAP);
+
+ /* Don't need to run motion sense task for a while */
+ task_wait_event(500 * MSEC);
+ }
+}
+
+/*****************************************************************************/
+/* Console commands */
+static int command_tap_info(int argc, char **argv)
+{
+ int odr, val;
+
+ ccprintf("tap: %s\n", (tap_detection && !lid_is_open()) ?
+ "on" : "off");
+
+ if (argc > 1) {
+ if (!parse_bool(argv[1], &val))
+ return EC_ERROR_PARAM1;
+ tap_debug = val;
+ }
+
+ ccprintf("debug: %s\n", tap_debug ? "on" : "off");
+ sensor->drv->get_data_rate(sensor, &odr);
+ ccprintf("odr: %d\n", odr);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(tapinfo, command_tap_info,
+ "debug on/off",
+ "Print tap information", NULL);
+
diff --git a/common/motion_sense.c b/common/motion_sense.c
index 95934dd60d..95773e2c4c 100644
--- a/common/motion_sense.c
+++ b/common/motion_sense.c
@@ -6,8 +6,10 @@
/* Motion sense module to read from various motion sensors. */
#include "accelgyro.h"
+#include "chipset.h"
#include "common.h"
#include "console.h"
+#include "gesture.h"
#include "hooks.h"
#include "host_command.h"
#include "lid_angle.h"
@@ -42,9 +44,15 @@ static int lid_angle_is_reliable;
#define MIN_POLLING_INTERVAL_MS 5
#define MAX_POLLING_INTERVAL_MS 1000
+/* Define sensor sampling interval in suspend. */
+#ifdef CONFIG_GESTURE_DETECTION
+#define SUSPEND_SAMPLING_INTERVAL CONFIG_GESTURE_SAMPLING_INTERVAL_MS
+#else
+#define SUSPEND_SAMPLING_INTERVAL 100
+#endif
+
/* Accelerometer polling intervals based on chipset state. */
static int accel_interval_ap_on_ms = 10;
-static const int accel_interval_ap_suspend_ms = 100;
/*
* Angle threshold for how close the hinge aligns with gravity before
@@ -171,6 +179,11 @@ static void clock_chipset_shutdown(void)
sensor->drv->set_data_rate(sensor, 0, 0);
sensor->state = SENSOR_NOT_INITIALIZED;
}
+
+#ifdef CONFIG_GESTURE_DETECTION
+ /* run gesture module hook which may override default behavior */
+ gesture_chipset_shutdown();
+#endif
}
DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, clock_chipset_shutdown, HOOK_PRIO_DEFAULT);
@@ -178,7 +191,8 @@ static void clock_chipset_suspend(void)
{
int i;
struct motion_sensor_t *sensor;
- accel_interval_ms = accel_interval_ap_suspend_ms;
+
+ accel_interval_ms = SUSPEND_SAMPLING_INTERVAL;
for (i = 0; i < motion_sensor_count; i++) {
sensor = &motion_sensors[i];
@@ -191,6 +205,11 @@ static void clock_chipset_suspend(void)
sensor->state = SENSOR_NOT_INITIALIZED;
}
}
+
+#ifdef CONFIG_GESTURE_DETECTION
+ /* run gesture module hook which may override default behavior */
+ gesture_chipset_suspend();
+#endif
}
DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, clock_chipset_suspend, HOOK_PRIO_DEFAULT);
@@ -205,6 +224,11 @@ static void clock_chipset_resume(void)
sensor = &motion_sensors[i];
sensor->active = SENSOR_ACTIVE_S0;
}
+
+#ifdef CONFIG_GESTURE_DETECTION
+ /* run gesture module hook which may override default behavior */
+ gesture_chipset_resume();
+#endif
}
DECLARE_HOOK(HOOK_CHIPSET_RESUME, clock_chipset_resume, HOOK_PRIO_DEFAULT);
@@ -331,7 +355,8 @@ void motion_sense_task(void)
set_present(lpc_status);
/* Initialize sampling interval. */
- accel_interval_ms = accel_interval_ap_suspend_ms;
+ accel_interval_ms = chipset_in_state(CHIPSET_STATE_ON) ?
+ accel_interval_ap_on_ms : SUSPEND_SAMPLING_INTERVAL;
while (1) {
ts0 = get_time();
@@ -364,10 +389,13 @@ void motion_sense_task(void)
sizeof(vector_3_t));
}
- if (rd_cnt != motion_sensor_count) {
- task_wait_event(TASK_MOTION_SENSE_WAIT_TIME);
- continue;
- }
+#ifdef CONFIG_GESTURE_DETECTION
+ /* Run gesture recognition engine */
+ gesture_calc();
+#endif
+
+ if (rd_cnt != motion_sensor_count)
+ goto motion_wait;
/* Calculate angle of lid accel. */
lid_angle_is_reliable = calculate_lid_angle(
@@ -405,6 +433,7 @@ void motion_sense_task(void)
#endif
update_sense_data(lpc_status, lpc_data, &sample_id);
+motion_wait:
/* Delay appropriately to keep sampling time consistent. */
ts1 = get_time();
wait_us = accel_interval_ms * MSEC - (ts1.val-ts0.val);
diff --git a/include/accelgyro.h b/include/accelgyro.h
index 940a094d82..6118f6dc0c 100644
--- a/include/accelgyro.h
+++ b/include/accelgyro.h
@@ -71,7 +71,7 @@ struct accelgyro_drv {
* Setter and getter methods for the sensor output data range. As the
* ODR increases, the LPF roll-off frequency also increases.
* @s Pointer to sensor data.
- * @rate Output data rate (units are Hz)
+ * @rate Output data rate (units are milli-Hz)
* @rnd Rounding flag. If true, it rounds up to nearest valid
* value. Otherwise, it rounds down.
* @return EC_SUCCESS if successful, non-zero if error.
diff --git a/include/config.h b/include/config.h
index 2cc2b0f93a..4522e69b5e 100644
--- a/include/config.h
+++ b/include/config.h
@@ -539,6 +539,14 @@
#undef CONFIG_FW_WP_RO_SIZE
/*****************************************************************************/
+/* Motion sensor based gesture recognition */
+#undef CONFIG_GESTURE_DETECTION
+
+/* Sensor sampling interval for gesture recognition */
+#undef CONFIG_GESTURE_SAMPLING_INTERVAL_MS
+
+
+/*****************************************************************************/
/*
* Support the host asking the EC about the status of the most recent host
diff --git a/include/gesture.h b/include/gesture.h
new file mode 100644
index 0000000000..d1b547d9cc
--- /dev/null
+++ b/include/gesture.h
@@ -0,0 +1,31 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Header for gesture.c */
+
+#ifndef __CROS_EC_GESTURE_H
+#define __CROS_EC_GESTURE_H
+
+/**
+ * Run gesture detection engine.
+ */
+void gesture_calc(void);
+
+/**
+ * Gesture hook to call on chipset resume.
+ */
+void gesture_chipset_resume(void);
+
+/**
+ * Gesture hook to call on chipset suspend.
+ */
+void gesture_chipset_suspend(void);
+
+/**
+ * Gesture hook to call on chipset shutdown.
+ */
+void gesture_chipset_shutdown(void);
+
+#endif /* __CROS_EC_GESTURE_H */