Index: apps/lang/deutsch.lang =================================================================== --- apps/lang/deutsch.lang (revision 14682) +++ apps/lang/deutsch.lang (working copy) @@ -11166,3 +11166,20 @@ rtc: "Uhr" + + id: LANG_WHEEL_ACCEL_START + desc: wheel acceleration start + user: + + *: none + scrollwheel: "Wheelspeed for Acceleration" + + + *: none + scrollwheel: "Startwert für Listenbeschleunigung" + + + *: none + scrollwheel: "Startwert für Listenbeschleunigung" + + Index: apps/lang/english.lang =================================================================== --- apps/lang/english.lang (revision 14682) +++ apps/lang/english.lang (working copy) @@ -11242,3 +11242,20 @@ usbstack: "Device Driver" + + id: LANG_WHEEL_ACCEL_START + desc: wheel acceleration start + user: + + *: none + scrollwheel: "Wheelspeed for Acceleration" + + + *: none + scrollwheel: "Wheelspeed for Acceleration" + + + *: none + scrollwheel: "Wheelspeed for Acceleration" + + Index: apps/gui/list.c =================================================================== --- apps/gui/list.c (revision 14682) +++ apps/gui/list.c (working copy) @@ -898,8 +898,7 @@ 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(), global_settings.wheel_accel_start); #else static int next_item_modifier = 1; static int last_accel_tick = 0; Index: apps/settings.h =================================================================== --- apps/settings.h (revision 14682) +++ apps/settings.h (working copy) @@ -747,6 +747,8 @@ #ifndef HAVE_SCROLLWHEEL int list_accel_start_delay; /* ms before we start increaseing step size */ int list_accel_wait; /* ms between increases */ +#else + unsigned int wheel_accel_start; /* value in clicks/sec from which acceleration starts */ #endif #ifdef HAVE_USBSTACK int usb_stack_mode; /* device or host */ Index: apps/menus/display_menu.c =================================================================== --- apps/menus/display_menu.c (revision 14682) +++ apps/menus/display_menu.c (working copy) @@ -317,6 +317,8 @@ MENUITEM_SETTING(list_accel_start_delay, &global_settings.list_accel_start_delay, NULL); MENUITEM_SETTING(list_accel_wait, &global_settings.list_accel_wait, NULL); +#else +MENUITEM_SETTING(wheel_accel_start, &global_settings.wheel_accel_start, NULL); #endif /* HAVE_SCROLLWHEEL */ #ifdef HAVE_LCD_BITMAP int screenscroll_callback(int action,const struct menu_item_ex *this_item) @@ -354,6 +356,8 @@ &scroll_paginated, #ifndef HAVE_SCROLLWHEEL &list_accel_start_delay, &list_accel_wait +#else + &wheel_accel_start #endif ); /* SCROLL MENU */ Index: apps/settings_list.c =================================================================== --- apps/settings_list.c (revision 14682) +++ apps/settings_list.c (working copy) @@ -242,6 +242,12 @@ (void)unit; snprintf(buffer, buffer_size, "%ds", ff_rewind_min_stepvals[val]); } +static long wheel_accel_getlang(int value) +{ + if (value == 0) + return LANG_OFF; + return TALK_ID(value, UNIT_INT); +} static long scanaccel_getlang(int value) { if (value == 0) @@ -257,6 +263,15 @@ else snprintf(buffer, buffer_size, "2x/%ds", val); } +static void wheel_accel_formatter(char *buffer, size_t buffer_size, + int val, const char *unit) +{ + (void)unit; + if (val == 0) + strcpy(buffer, str(LANG_OFF)); + else + snprintf(buffer, buffer_size, "%d °/s", val); +} static const unsigned char poweroff_idle_timer_times[] = {0,1,2,3,4,5,6,7,8,9,10,15,30,45,60}; static long poweroff_idle_timer_getlang(int value) @@ -1262,6 +1277,10 @@ INT_SETTING(0, list_accel_wait, LANG_LISTACCEL_ACCEL_SPEED, 3, "list_accel_wait", UNIT_SEC, 1, 10, 1, scanaccel_formatter, scanaccel_getlang, NULL), +#else + INT_SETTING(0, wheel_accel_start, LANG_WHEEL_ACCEL_START, DEFAULT_WHEEL_ACCEL_START, + "wheel_accel_start", UNIT_INT, 180, 720, 90, + wheel_accel_formatter, wheel_accel_getlang, NULL), #endif /* HAVE_SCROLLWHEEL */ #ifdef HAVE_USBSTACK CHOICE_SETTING(0, usb_stack_mode, LANG_USBSTACK_MODE, 0, "usb mode", Index: firmware/export/config-ipodmini2g.h =================================================================== --- firmware/export/config-ipodmini2g.h (revision 14682) +++ firmware/export/config-ipodmini2g.h (working copy) @@ -75,6 +75,11 @@ /* 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 DEFAULT_WHEEL_ACCEL_START 360 + /* Define this if you can detect headphones */ #define HAVE_HEADPHONE_DETECTION Index: firmware/export/config-ipodnano.h =================================================================== --- firmware/export/config-ipodnano.h (revision 14682) +++ firmware/export/config-ipodnano.h (working copy) @@ -73,6 +73,11 @@ /* 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 DEFAULT_WHEEL_ACCEL_START 360 + /* Define this if you can detect headphones */ #define HAVE_HEADPHONE_DETECTION Index: firmware/export/button.h =================================================================== --- firmware/export/button.h (revision 14682) +++ firmware/export/button.h (working copy) @@ -51,7 +51,7 @@ #endif #ifdef HAVE_SCROLLWHEEL -int button_apply_acceleration(unsigned int data, unsigned int factor); +int button_apply_acceleration(const unsigned int data, unsigned int factor); #endif #define BUTTON_NONE 0x00000000 Index: firmware/export/config-ipod4g.h =================================================================== --- firmware/export/config-ipod4g.h (revision 14682) +++ firmware/export/config-ipod4g.h (working copy) @@ -78,6 +78,11 @@ /* 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 DEFAULT_WHEEL_ACCEL_START 360 + /* Define this if you can detect headphones */ #define HAVE_HEADPHONE_DETECTION Index: firmware/export/config-e200.h =================================================================== --- firmware/export/config-e200.h (revision 14682) +++ firmware/export/config-e200.h (working copy) @@ -97,9 +97,8 @@ /* 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 DEFAULT_WHEEL_ACCEL_START 540 /* define this if you have a flash memory storage */ #define HAVE_FLASH_STORAGE Index: firmware/export/config-ipodvideo.h =================================================================== --- firmware/export/config-ipodvideo.h (revision 14682) +++ firmware/export/config-ipodvideo.h (working copy) @@ -73,6 +73,11 @@ /* 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 DEFAULT_WHEEL_ACCEL_START 360 + /* Define this if you can detect headphones */ #define HAVE_HEADPHONE_DETECTION Index: firmware/export/config-ipodcolor.h =================================================================== --- firmware/export/config-ipodcolor.h (revision 14682) +++ firmware/export/config-ipodcolor.h (working copy) @@ -70,6 +70,11 @@ /* 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 DEFAULT_WHEEL_ACCEL_START 360 + /* Define this if you can detect headphones */ #define HAVE_HEADPHONE_DETECTION Index: firmware/target/arm/ipod/button-clickwheel.c =================================================================== --- firmware/target/arm/ipod/button-clickwheel.c (revision 14682) +++ firmware/target/arm/ipod/button-clickwheel.c (working copy) @@ -40,6 +40,19 @@ #include "system.h" #include "powermgmt.h" +#define WHEEL_FAST_OFF_INTERVAL 250000 /* timeout for acceleration = 200ms */ +#define WHEELCLICKS_PER_ROTATION 96 /* wheelclicks per full rotation */ + +static int old_wheel_value = -1; +static int repeat = 0; +static int wheel_delta = 0; +#ifdef HAVE_SCROLLWHEEL + static int accumulated_wheel_delta = 0; + static unsigned int wheel_repeat = 0; + static unsigned long last_wheel_usec = 0; + static unsigned long wheel_velocity = 0; +#endif + /* Variable to use for setting button status in interrupt handler */ int int_btn = BUTTON_NONE; #ifdef HAVE_WHEEL_POSITION @@ -96,9 +109,6 @@ 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 & 0x100) btn |= BUTTON_SELECT; if (status & 0x200) @@ -113,47 +123,107 @@ /* NB: highest wheel = 0x5F, clockwise increases */ int new_wheel_value = (status << 9) >> 25; 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) { + 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; - + wheel_delta = new_wheel_value - old_wheel_value; + unsigned int wheel_keycode = BUTTON_NONE; + 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) { + + /* getting wheel_keycode from wheel_delta */ + if (wheel_delta > 4) wheel_keycode = BUTTON_SCROLL_FWD; - } else if (wheel_delta < -4) { + else if (wheel_delta < -4) wheel_keycode = BUTTON_SCROLL_BACK; - } else goto wheel_end; + else + wheel_keycode = BUTTON_NONE; + + if (wheel_keycode != BUTTON_NONE) + { +#ifdef HAVE_SCROLLWHEEL + unsigned long usec = USEC_TIMER; + long v = (usec - last_wheel_usec) & 0x7fffffff; + + /* add the current wheel_delta */ + accumulated_wheel_delta += wheel_delta; + v = v ? (1000000 * accumulated_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 */ + v = (v>0xffffff) ? 0xffffff : v; /* limit to 24 bit */ + + /* some velocity filtering to smooth things out */ + wheel_velocity = (7*wheel_velocity + v) / 8; + + /* direction reversals nullify acceleration and accumulator */ + if (wheel_keycode != wheel_repeat) + { + wheel_repeat = wheel_keycode; + wheel_velocity = 0; + accumulated_wheel_delta = 0; + } + /* timeout nullifies acceleration and accumulator */ + if (TIME_AFTER(usec, last_wheel_usec + WHEEL_FAST_OFF_INTERVAL)) + { + wheel_velocity = 0; + accumulated_wheel_delta = 0; + } +#endif + #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)) + { +#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); + + accumulated_wheel_delta = 0; +#else + queue_post(&button_queue, wheel_keycode | repeat, + (wheel_delta << 16) | new_wheel_value); +#endif + } + + if (!repeat) repeat = BUTTON_REPEAT; +#ifdef HAVE_SCROLLWHEEL + last_wheel_usec = usec; +#endif } - - if (!wheel_repeat) wheel_repeat = BUTTON_REPEAT; + else + { + /* take from former revision */ + goto wheel_end; + } } old_wheel_value = new_wheel_value; - } else if (old_wheel_value >= 0) { - /* scroll wheel up */ + } + else if (old_wheel_value >= 0) + { + // scroll wheel up old_wheel_value = -1; - wheel_repeat = 0; + repeat = 0; } - } else if (status == 0xffffffff) { + } + else if (status == 0xffffffff) + { opto_i2c_init(); } } @@ -200,14 +270,23 @@ 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; + +#ifdef HAVE_SCROLLWHEEL + /* Get current tick before enabling button interrupts */ + last_wheel_usec = USEC_TIMER; +#endif + /* enable interrupts */ GPIOA_INT_EN = 0x20; + /* unmask interrupt */ CPU_INT_EN = 0x40000000; CPU_HI_INT_EN = I2C_MASK; Index: firmware/target/arm/sandisk/sansa-e200/button-e200.c =================================================================== --- firmware/target/arm/sandisk/sansa-e200/button-e200.c (revision 14682) +++ firmware/target/arm/sandisk/sansa-e200/button-e200.c (working copy) @@ -26,6 +26,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 @@ -136,11 +137,10 @@ unsigned long usec = USEC_TIMER; unsigned v = (usec - last_wheel_usec) & 0x7fffffff; - /* wheel velocity in 0.24 fixed point - clicks/uS */ + v = (v>0) ? 1000000 / v : 0; /* clicks/sec = 1000000 * clicks/usec */ + v = (v * 360) / WHEELCLICKS_PER_ROTATION; /* conversion to degree/sec */ + v = (v>0xffffff) ? 0xffffff : v; /* limit to 24 bit */ - /* velocity cap to 18 bits to allow up to x16 scaling */ - v = (v < 0x40) ? 0xffffff / 0x40 : 0xffffff / v; - /* some velocity filtering to smooth things out */ wheel_velocity = (7*wheel_velocity + v) / 8; @@ -172,12 +172,12 @@ 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) { @@ -189,7 +189,7 @@ } 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*/ Index: firmware/drivers/button.c =================================================================== --- firmware/drivers/button.c (revision 14682) +++ firmware/drivers/button.c (working copy) @@ -490,22 +490,31 @@ * 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 + * Value in degree/sec -- configurable via settings (180...720) -- above + * which the accelerated scrolling starts. Factor is internally scaled by + * 1<<22 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, unsigned int factor) { int delta = (data >> 24) & 0x7f; + + /* factor = 2^22 / (global_settings.wheel_accel_start)^2 */ + /* 2^22 needed as normalization */ + /* factor = 8...129 (180 degree/sec ... 720 degree/sec) */ + factor = (1<<22)/(factor*factor); if ((data & (1 << 31)) != 0) { unsigned int v = data & 0xffffff; - v = factor * (unsigned long long)v*v / 0xffffffffffffull; + /* limit velocity to 11 bit to prevent from overflow */ + /* 11 bits maximum = 2047 -> >5 full rotations/sec */ + /* factor [8 bit] * v [11 bit] * v [11 bit] */ + v = (v<=0x7ff) ? v : 0x7ff; + v = (factor * v*v)>>22; if (v > 1) delta *= v;