FS#7738 - Scroll wheel acceleration for iPod
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15681 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
a8c020288c
commit
e75327b332
11 changed files with 231 additions and 67 deletions
|
@ -1011,8 +1011,7 @@ bool gui_synclist_do_button(struct gui_synclist * lists,
|
|||
int i;
|
||||
|
||||
#ifdef HAVE_SCROLLWHEEL
|
||||
int next_item_modifier = button_apply_acceleration(get_action_data(),
|
||||
WHEEL_ACCELERATION_FACTOR);
|
||||
int next_item_modifier = button_apply_acceleration(get_action_data());
|
||||
#else
|
||||
static int next_item_modifier = 1;
|
||||
static int last_accel_tick = 0;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
||||
/* 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_keycode = BUTTON_SCROLL_FWD;
|
||||
} else if (wheel_delta < -4) {
|
||||
wheel_keycode = BUTTON_SCROLL_BACK;
|
||||
} else goto wheel_end;
|
||||
|
||||
#ifdef HAVE_WHEEL_POSITION
|
||||
if (send_events)
|
||||
#endif
|
||||
{
|
||||
data = (wheel_delta << 16) | new_wheel_value;
|
||||
queue_post(&button_queue, wheel_keycode | wheel_repeat,
|
||||
data);
|
||||
}
|
||||
|
||||
if (!wheel_repeat) wheel_repeat = BUTTON_REPEAT;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
old_wheel_value = new_wheel_value;
|
||||
} else if (old_wheel_value >= 0) {
|
||||
/* scroll wheel up */
|
||||
old_wheel_value = -1;
|
||||
wheel_repeat = 0;
|
||||
if (old_wheel_value >= 0)
|
||||
{
|
||||
/* This is for later = BUTTON_SCROLL_TOUCH;*/
|
||||
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 <= -WHEEL_SENSITIVITY)
|
||||
wheel_keycode = BUTTON_SCROLL_BACK;
|
||||
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)
|
||||
#endif
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue