summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
authorLinus Nielsen Feltzing <linus@haxx.se>2007-11-19 11:05:54 +0000
committerLinus Nielsen Feltzing <linus@haxx.se>2007-11-19 11:05:54 +0000
commite75327b33249cb2656281af2bd5368fe0edd2d48 (patch)
tree80037d0f7a7420886b678a195ce2bbb90deba951 /firmware
parenta8c020288c729f077b067f45f92a47bca56caae1 (diff)
downloadrockbox-e75327b33249cb2656281af2bd5368fe0edd2d48.zip
rockbox-e75327b33249cb2656281af2bd5368fe0edd2d48.tar.gz
rockbox-e75327b33249cb2656281af2bd5368fe0edd2d48.tar.bz2
rockbox-e75327b33249cb2656281af2bd5368fe0edd2d48.tar.xz
FS#7738 - Scroll wheel acceleration for iPod
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15681 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware')
-rw-r--r--firmware/drivers/button.c28
-rw-r--r--firmware/export/button.h2
-rw-r--r--firmware/export/config-e200.h7
-rw-r--r--firmware/export/config-ipod4g.h7
-rw-r--r--firmware/export/config-ipodcolor.h7
-rw-r--r--firmware/export/config-ipodmini2g.h7
-rw-r--r--firmware/export/config-ipodnano.h7
-rw-r--r--firmware/export/config-ipodvideo.h7
-rw-r--r--firmware/target/arm/ipod/button-clickwheel.c194
-rw-r--r--firmware/target/arm/sandisk/sansa-e200/button-e200.c15
10 files changed, 223 insertions, 58 deletions
diff --git a/firmware/drivers/button.c b/firmware/drivers/button.c
index 000c478..c7d532e 100644
--- a/firmware/drivers/button.c
+++ b/firmware/drivers/button.c
@@ -522,26 +522,40 @@ void button_clear_queue(void)
#endif /* SIMULATOR */
#ifdef HAVE_SCROLLWHEEL
+/* WHEEL_ACCEL_FACTOR = 2^16 / WHEEL_ACCEL_START */
+#define WHEEL_ACCEL_FACTOR (1<<16)/WHEEL_ACCEL_START
/**
* data:
* [31] Use acceleration
* [30:24] Message post count (skipped + 1) (1-127)
- * [23:0] Velocity - clicks/uS - 0.24 fixed point
+ * [23:0] Velocity - degree/sec
*
- * factor:
- * Wheel acceleration scaling factor - x.24 fixed point -
- * no greater than what will not overflow 64 bits when multiplied
- * by the driver's maximum velocity in (clicks/usec)^2 in 0.24
+ * WHEEL_ACCEL_FACTOR:
+ * Value in degree/sec -- configurable via settings -- above which
+ * the accelerated scrolling starts. Factor is internally scaled by
+ * 1<<16 in respect to the following 32bit integer operations.
*/
-int button_apply_acceleration(unsigned int data, unsigned int factor)
+int button_apply_acceleration(const unsigned int data)
{
int delta = (data >> 24) & 0x7f;
if ((data & (1 << 31)) != 0)
{
+ /* read driver's velocity from data */
unsigned int v = data & 0xffffff;
- v = factor * (unsigned long long)v*v / 0xffffffffffffull;
+ /* v = 28.4 fixed point */
+ v = (WHEEL_ACCEL_FACTOR * v)>>(16-4);
+
+ /* Calculate real numbers item to scroll based upon acceleration
+ * setting, use correct roundoff */
+#if (WHEEL_ACCELERATION == 1)
+ v = (v*v + (1<< 7))>> 8;
+#elif (WHEEL_ACCELERATION == 2)
+ v = (v*v*v + (1<<11))>>12;
+#elif (WHEEL_ACCELERATION == 3)
+ v = (v*v*v*v + (1<<15))>>16;
+#endif
if (v > 1)
delta *= v;
diff --git a/firmware/export/button.h b/firmware/export/button.h
index a38385c..f50f3dd 100644
--- a/firmware/export/button.h
+++ b/firmware/export/button.h
@@ -52,7 +52,7 @@ void wheel_send_events(bool send);
#endif
#ifdef HAVE_SCROLLWHEEL
-int button_apply_acceleration(unsigned int data, unsigned int factor);
+int button_apply_acceleration(const unsigned int data);
#endif
#define BUTTON_NONE 0x00000000
diff --git a/firmware/export/config-e200.h b/firmware/export/config-e200.h
index e2274bc..f5ba8f0 100644
--- a/firmware/export/config-e200.h
+++ b/firmware/export/config-e200.h
@@ -100,9 +100,10 @@
/* define this if the unit uses a scrollwheel for navigation */
#define HAVE_SCROLLWHEEL
-/* define wheel acceleration scaling factor */
-/* Range for this target: 0xffffff*(0.0-16.000000894069724921567733381255) */
-#define WHEEL_ACCELERATION_FACTOR (0xffffff*7)
+/* define from which rotation speed [degree/sec] on the acceleration starts */
+#define WHEEL_ACCEL_START 540
+/* define type of acceleration (1 = ^2, 2 = ^3, 3 = ^4) */
+#define WHEEL_ACCELERATION 1
/* define this if you have a flash memory storage */
#define HAVE_FLASH_STORAGE
diff --git a/firmware/export/config-ipod4g.h b/firmware/export/config-ipod4g.h
index fc28050..7ed7901 100644
--- a/firmware/export/config-ipod4g.h
+++ b/firmware/export/config-ipod4g.h
@@ -86,6 +86,13 @@
/* Define this for LCD backlight available */
#define HAVE_BACKLIGHT
+/* define this if the unit uses a scrollwheel for navigation */
+#define HAVE_SCROLLWHEEL
+/* define from which rotation speed [degree/sec] on the acceleration starts */
+#define WHEEL_ACCEL_START 270
+/* define type of acceleration (1 = ^2, 2 = ^3, 3 = ^4) */
+#define WHEEL_ACCELERATION 3
+
/* Define this if you can detect headphones */
#define HAVE_HEADPHONE_DETECTION
diff --git a/firmware/export/config-ipodcolor.h b/firmware/export/config-ipodcolor.h
index 0aba8c3..25a7fb3 100644
--- a/firmware/export/config-ipodcolor.h
+++ b/firmware/export/config-ipodcolor.h
@@ -73,6 +73,13 @@
/* Define this for LCD backlight available */
#define HAVE_BACKLIGHT
+/* define this if the unit uses a scrollwheel for navigation */
+#define HAVE_SCROLLWHEEL
+/* define from which rotation speed [degree/sec] on the acceleration starts */
+#define WHEEL_ACCEL_START 270
+/* define type of acceleration (1 = ^2, 2 = ^3, 3 = ^4) */
+#define WHEEL_ACCELERATION 3
+
/* Define this if you can detect headphones */
#define HAVE_HEADPHONE_DETECTION
diff --git a/firmware/export/config-ipodmini2g.h b/firmware/export/config-ipodmini2g.h
index 5dd2293..0d3dceb 100644
--- a/firmware/export/config-ipodmini2g.h
+++ b/firmware/export/config-ipodmini2g.h
@@ -83,6 +83,13 @@
/* We can fade the backlight by using PWM */
#define HAVE_BACKLIGHT_PWM_FADING
+/* define this if the unit uses a scrollwheel for navigation */
+#define HAVE_SCROLLWHEEL
+/* define from which rotation speed [degree/sec] on the acceleration starts */
+#define WHEEL_ACCEL_START 270
+/* define type of acceleration (1 = ^2, 2 = ^3, 3 = ^4) */
+#define WHEEL_ACCELERATION 3
+
/* Define this if you can detect headphones */
#define HAVE_HEADPHONE_DETECTION
diff --git a/firmware/export/config-ipodnano.h b/firmware/export/config-ipodnano.h
index 6909561..27e1594 100644
--- a/firmware/export/config-ipodnano.h
+++ b/firmware/export/config-ipodnano.h
@@ -82,6 +82,13 @@
/* We can fade the backlight by using PWM */
#define HAVE_BACKLIGHT_PWM_FADING
+/* define this if the unit uses a scrollwheel for navigation */
+#define HAVE_SCROLLWHEEL
+/* define from which rotation speed [degree/sec] on the acceleration starts */
+#define WHEEL_ACCEL_START 270
+/* define type of acceleration (1 = ^2, 2 = ^3, 3 = ^4) */
+#define WHEEL_ACCELERATION 3
+
/* Define this if you can detect headphones */
#define HAVE_HEADPHONE_DETECTION
diff --git a/firmware/export/config-ipodvideo.h b/firmware/export/config-ipodvideo.h
index 60a5dd8..c66f6d9 100644
--- a/firmware/export/config-ipodvideo.h
+++ b/firmware/export/config-ipodvideo.h
@@ -82,6 +82,13 @@
/* We can fade the backlight by using PWM */
#define HAVE_BACKLIGHT_PWM_FADING
+/* define this if the unit uses a scrollwheel for navigation */
+#define HAVE_SCROLLWHEEL
+/* define from which rotation speed [degree/sec] on the acceleration starts */
+#define WHEEL_ACCEL_START 270
+/* define type of acceleration (1 = ^2, 2 = ^3, 3 = ^4) */
+#define WHEEL_ACCELERATION 3
+
/* Define this if you can detect headphones */
#define HAVE_HEADPHONE_DETECTION
diff --git a/firmware/target/arm/ipod/button-clickwheel.c b/firmware/target/arm/ipod/button-clickwheel.c
index 15f3cdd..f01666a 100644
--- a/firmware/target/arm/ipod/button-clickwheel.c
+++ b/firmware/target/arm/ipod/button-clickwheel.c
@@ -40,6 +40,30 @@
#include "system.h"
#include "powermgmt.h"
+#define WHEEL_FAST_OFF_TIMEOUT 250000 /* timeout for acceleration = 250ms */
+#define WHEEL_REPEAT_TIMEOUT 250000 /* timeout for button repeat = 250ms */
+#define WHEEL_UNTOUCH_TIMEOUT 150000 /* timeout for untouching wheel = 100ms */
+#define WHEELCLICKS_PER_ROTATION 96 /* wheelclicks per full rotation */
+
+/* This amount of clicks is needed for at least scrolling 1 item. Choose small values
+ * to have high sensitivity but few precision, choose large values to have less
+ * sensitivity and good precision. */
+#if defined(IPOD_NANO)
+#define WHEEL_SENSITIVITY 6 /* iPod nano has smaller wheel, lower sensitivity needed */
+#else
+#define WHEEL_SENSITIVITY 4 /* default sensitivity */
+#endif
+
+int old_wheel_value = -1;
+int new_wheel_value = 0;
+int repeat = 0;
+int wheel_delta = 0;
+bool wheel_is_touched = false;
+unsigned int accumulated_wheel_delta = 0;
+unsigned int wheel_repeat = 0;
+unsigned int wheel_velocity = 0;
+unsigned long last_wheel_usec = 0;
+
/* Variable to use for setting button status in interrupt handler */
int int_btn = BUTTON_NONE;
#ifdef HAVE_WHEEL_POSITION
@@ -89,16 +113,15 @@ static inline int ipod_4g_button_read(void)
int btn = BUTTON_NONE;
unsigned reg = 0x7000c104;
- if ((inl(0x7000c104) & 0x4000000) != 0) {
+ if ((inl(0x7000c104) & 0x4000000) != 0)
+ {
unsigned status = inl(0x7000c140);
reg = reg + 0x3C; /* 0x7000c140 */
outl(0x0, 0x7000c140); /* clear interrupt status? */
- if ((status & 0x800000ff) == 0x8000001a) {
- static int old_wheel_value IDATA_ATTR = -1;
- static int wheel_repeat = 0;
-
+ if ((status & 0x800000ff) == 0x8000001a)
+ {
if (status & 0x100)
btn |= BUTTON_SELECT;
if (status & 0x200)
@@ -109,63 +132,152 @@ static inline int ipod_4g_button_read(void)
btn |= BUTTON_PLAY;
if (status & 0x1000)
btn |= BUTTON_MENU;
- if (status & 0x40000000) {
- /* NB: highest wheel = 0x5F, clockwise increases */
- int new_wheel_value = (status << 9) >> 25;
+ if (status & 0x40000000)
+ {
+ unsigned long usec = USEC_TIMER;
+
+ /* Highest wheel = 0x5F, clockwise increases */
+ new_wheel_value = (status >> 16) & 0x7f;
whl = new_wheel_value;
+
+ /* switch on backlight (again), reset power-off timer */
backlight_on();
reset_poweroff_timer();
- /* The queue should have no other events when scrolling */
- if (queue_empty(&button_queue) && old_wheel_value >= 0) {
+
+ /* Check whether the scrollwheel was untouched by accident or by will. */
+ /* This is needed because wheel may be untoched very shortly during rotation */
+ if ( (!wheel_is_touched) && TIME_AFTER(usec, last_wheel_usec + WHEEL_UNTOUCH_TIMEOUT) )
+ {
+ /* wheel has been really untouched -> reset internal variables */
+ old_wheel_value = -1;
+ wheel_velocity = 0;
+ accumulated_wheel_delta = 0;
+ wheel_repeat = BUTTON_NONE;
+ }
+ else
+ {
+ /* wheel was shortly untouched by accident -> leave internal variables */
+ wheel_is_touched = true;
+ }
+ if (old_wheel_value >= 0)
+ {
/* This is for later = BUTTON_SCROLL_TOUCH;*/
- int wheel_delta = new_wheel_value - old_wheel_value;
- unsigned long data;
- int wheel_keycode;
-
- if (wheel_delta < -48)
- wheel_delta += 96; /* Forward wrapping case */
- else if (wheel_delta > 48)
- wheel_delta -= 96; /* Backward wrapping case */
-
- if (wheel_delta > 4) {
+ wheel_delta = new_wheel_value - old_wheel_value;
+ unsigned int wheel_keycode = BUTTON_NONE;
+
+ /* Taking into account wrapping during transition from highest
+ * to lowest wheel position and back */
+ if (wheel_delta < -WHEELCLICKS_PER_ROTATION/2)
+ wheel_delta += WHEELCLICKS_PER_ROTATION; /* Forward wrapping case */
+ else if (wheel_delta > WHEELCLICKS_PER_ROTATION/2)
+ wheel_delta -= WHEELCLICKS_PER_ROTATION; /* Backward wrapping case */
+
+ /* Getting direction and wheel_keycode from wheel_delta.
+ * Need at least some clicks to be sure to avoid haptic fuzziness */
+ if (wheel_delta >= WHEEL_SENSITIVITY)
wheel_keycode = BUTTON_SCROLL_FWD;
- } else if (wheel_delta < -4) {
+ else if (wheel_delta <= -WHEEL_SENSITIVITY)
wheel_keycode = BUTTON_SCROLL_BACK;
- } else goto wheel_end;
+ else
+ wheel_keycode = BUTTON_NONE;
+
+ if (wheel_keycode != BUTTON_NONE)
+ {
+ long v = (usec - last_wheel_usec) & 0x7fffffff;
+
+ /* undo signedness */
+ wheel_delta = (wheel_delta>0) ? wheel_delta : -wheel_delta;
+
+ /* add the current wheel_delta */
+ accumulated_wheel_delta += wheel_delta;
+
+ v = v ? (1000000 * wheel_delta) / v : 0; /* clicks/sec = 1000000 * clicks/usec */
+ v = (v * 360) / WHEELCLICKS_PER_ROTATION; /* conversion to degree/sec */
+ v = (v<0) ? -v : v; /* undo signedness */
+
+ /* some velocity filtering to smooth things out */
+ wheel_velocity = (31 * wheel_velocity + v) / 32;
+ /* limit to 24 bit */
+ wheel_velocity = (wheel_velocity>0xffffff) ? 0xffffff : wheel_velocity;
+
+ /* assume REPEAT = off */
+ repeat = 0;
+
+ /* direction reversals must nullify acceleration and accumulator */
+ if (wheel_keycode != wheel_repeat)
+ {
+ wheel_repeat = wheel_keycode;
+ wheel_velocity = 0;
+ accumulated_wheel_delta = 0;
+ }
+ /* on same direction REPEAT is assumed when new click is within timeout */
+ else if (TIME_BEFORE(usec, last_wheel_usec + WHEEL_REPEAT_TIMEOUT))
+ {
+ repeat = BUTTON_REPEAT;
+ }
+ /* timeout nullifies acceleration and accumulator */
+ if (TIME_AFTER(usec, last_wheel_usec + WHEEL_FAST_OFF_TIMEOUT))
+ {
+ wheel_velocity = 0;
+ accumulated_wheel_delta = 0;
+ }
#ifdef HAVE_WHEEL_POSITION
- if (send_events)
+ if (send_events)
#endif
- {
- data = (wheel_delta << 16) | new_wheel_value;
- queue_post(&button_queue, wheel_keycode | wheel_repeat,
- data);
+ /* The queue should have no other events when scrolling */
+ if (queue_empty(&button_queue))
+ {
+ /* each WHEEL_SENSITIVITY clicks = scrolling 1 item */
+ accumulated_wheel_delta /= WHEEL_SENSITIVITY;
+#ifdef HAVE_SCROLLWHEEL
+ /* use data-format for HAVE_SCROLLWHEEL */
+ /* always use acceleration mode (1<<31) */
+ /* always set message post count to (1<<24) for iPod */
+ /* this way the scrolling is always calculated from wheel_velocity */
+ queue_post(&button_queue, wheel_keycode | repeat,
+ (1<<31) | (1 << 24) | wheel_velocity);
+
+#else
+ queue_post(&button_queue, wheel_keycode | repeat,
+ (accumulated_wheel_delta << 16) | new_wheel_value);
+#endif
+ accumulated_wheel_delta = 0;
+ }
+ last_wheel_usec = usec;
+ old_wheel_value = new_wheel_value;
}
-
- if (!wheel_repeat) wheel_repeat = BUTTON_REPEAT;
}
-
- old_wheel_value = new_wheel_value;
- } else if (old_wheel_value >= 0) {
- /* scroll wheel up */
- old_wheel_value = -1;
- wheel_repeat = 0;
+ else
+ {
+ /* scrollwheel was touched for the first time after finger lifting */
+ old_wheel_value = new_wheel_value;
+ wheel_is_touched = true;
+ }
+ }
+ else
+ {
+ /* In this case the finger was lifted from the scrollwheel. */
+ wheel_is_touched = false;
}
- } else if (status == 0xffffffff) {
+ }
+ else if (status == 0xffffffff)
+ {
opto_i2c_init();
}
}
-wheel_end:
-
- if ((inl(reg) & 0x8000000) != 0) {
+ if ((inl(reg) & 0x8000000) != 0)
+ {
outl(0xffffffff, 0x7000c120);
outl(0xffffffff, 0x7000c124);
}
/* Save the new absolute wheel position */
+#ifdef HAVE_WHEEL_POSITION
wheel_position = whl;
+#endif
return btn;
}
@@ -200,14 +312,18 @@ void ipod_4g_button_int(void)
void button_init_device(void)
{
opto_i2c_init();
+
/* hold button - enable as input */
GPIOA_ENABLE |= 0x20;
GPIOA_OUTPUT_EN &= ~0x20;
+
/* hold button - set interrupt levels */
GPIOA_INT_LEV = ~(GPIOA_INPUT_VAL & 0x20);
GPIOA_INT_CLR = GPIOA_INT_STAT & 0x20;
+
/* enable interrupts */
GPIOA_INT_EN = 0x20;
+
/* unmask interrupt */
CPU_INT_EN = 0x40000000;
CPU_HI_INT_EN = I2C_MASK;
diff --git a/firmware/target/arm/sandisk/sansa-e200/button-e200.c b/firmware/target/arm/sandisk/sansa-e200/button-e200.c
index 5988e16..a74d860 100644
--- a/firmware/target/arm/sandisk/sansa-e200/button-e200.c
+++ b/firmware/target/arm/sandisk/sansa-e200/button-e200.c
@@ -27,6 +27,7 @@
#define WHEEL_REPEAT_INTERVAL 300000
#define WHEEL_FAST_ON_INTERVAL 20000
#define WHEEL_FAST_OFF_INTERVAL 60000
+#define WHEELCLICKS_PER_ROTATION 48 /* wheelclicks per full rotation */
/* Clickwheel */
#ifndef BOOTLOADER
@@ -137,10 +138,8 @@ void clickwheel_int(void)
unsigned long usec = USEC_TIMER;
unsigned v = (usec - last_wheel_usec) & 0x7fffffff;
- /* wheel velocity in 0.24 fixed point - clicks/uS */
-
- /* velocity cap to 18 bits to allow up to x16 scaling */
- v = (v < 0x40) ? 0xffffff / 0x40 : 0xffffff / v;
+ v = (v>0) ? 1000000 / v : 0; /* clicks/sec = 1000000 * clicks/usec */
+ v = (v>0xffffff) ? 0xffffff : v; /* limit to 24 bit */
/* some velocity filtering to smooth things out */
wheel_velocity = (7*wheel_velocity + v) / 8;
@@ -173,12 +172,12 @@ void clickwheel_int(void)
else
{
/* fast ON gets filtered to avoid inadvertent jumps to fast mode */
- if (repeat && wheel_velocity > 0xffffff/WHEEL_FAST_ON_INTERVAL)
+ if (repeat && wheel_velocity > 1000000/WHEEL_FAST_ON_INTERVAL)
{
/* moving into fast mode */
wheel_fast_mode = 1 << 31;
wheel_click_count = 0;
- wheel_velocity = 0xffffff/WHEEL_FAST_OFF_INTERVAL;
+ wheel_velocity = 1000000/WHEEL_FAST_OFF_INTERVAL;
}
else if (++wheel_click_count < 2)
{
@@ -190,7 +189,7 @@ void clickwheel_int(void)
}
if (TIME_AFTER(current_tick, next_backlight_on) ||
- v <= 0xffffff/(1000000/4))
+ v <= 4)
{
/* poke backlight to turn it on or maintain it no more often
than every 1/4 second*/
@@ -214,7 +213,7 @@ void clickwheel_int(void)
if (queue_empty(&button_queue))
{
queue_post(&button_queue, btn, wheel_fast_mode |
- (wheel_delta << 24) | wheel_velocity);
+ (wheel_delta << 24) | wheel_velocity*360/WHEELCLICKS_PER_ROTATION);
/* message posted - reset delta */
wheel_delta = 1;
}